diff --git a/examples/custom_sink/agentevals_example_custom_sink/sink.py b/examples/custom_sink/agentevals_example_custom_sink/sink.py index de666cf..0332a32 100644 --- a/examples/custom_sink/agentevals_example_custom_sink/sink.py +++ b/examples/custom_sink/agentevals_example_custom_sink/sink.py @@ -38,7 +38,7 @@ def __init__(self, path: Path) -> None: async def _write(self, payload: dict) -> None: async with self._lock: self._path.parent.mkdir(parents=True, exist_ok=True) - with self._path.open("a") as f: # noqa: ASYNC230 + with self._path.open("a", encoding="utf-8") as f: # noqa: ASYNC230 f.write(json.dumps(payload) + "\n") async def emit_partial(self, run_id: UUID, results: list[Result], attempt: int) -> None: diff --git a/src/agentevals/api/debug_routes.py b/src/agentevals/api/debug_routes.py index 4a3d8e6..c040536 100644 --- a/src/agentevals/api/debug_routes.py +++ b/src/agentevals/api/debug_routes.py @@ -114,7 +114,7 @@ def _collect_temp_files(session_ids: set[str] | None = None) -> dict[str, str]: if sid not in session_ids: continue try: - with open(path) as f: + with open(path, encoding="utf-8") as f: files[basename] = f.read() except OSError: logger.debug("Could not read temp file %s", path) diff --git a/src/agentevals/api/routes.py b/src/agentevals/api/routes.py index 29de1d2..b54c3cf 100644 --- a/src/agentevals/api/routes.py +++ b/src/agentevals/api/routes.py @@ -129,7 +129,7 @@ def _load_eval_set_dict(path: str | None) -> dict | None: if not path: return None try: - with open(path) as f: + with open(path, encoding="utf-8") as f: return json.load(f) except (OSError, json.JSONDecodeError): logger.warning("could not re-read eval_set file at %s for persistence", path) diff --git a/src/agentevals/api/streaming_routes.py b/src/agentevals/api/streaming_routes.py index 22746c2..831fcb5 100644 --- a/src/agentevals/api/streaming_routes.py +++ b/src/agentevals/api/streaming_routes.py @@ -225,7 +225,7 @@ async def evaluate_sessions( import tempfile - eval_set_file = tempfile.NamedTemporaryFile(mode="w", suffix=".json", delete=False) + eval_set_file = tempfile.NamedTemporaryFile(mode="w", suffix=".json", delete=False, encoding="utf-8") json.dump(eval_set_response.data.eval_set, eval_set_file) eval_set_file.close() @@ -334,7 +334,7 @@ async def prepare_evaluation( temp_dir = tempfile.gettempdir() eval_set_file = os.path.join(temp_dir, f"eval_set_{request.golden_session_id}.json") - with open(eval_set_file, "w") as f: # noqa: ASYNC230 + with open(eval_set_file, "w", encoding="utf-8") as f: # noqa: ASYNC230 json.dump(eval_set_response.data.eval_set, f) trace_files = [] @@ -415,7 +415,7 @@ async def get_trace( # evaluate-sessions path. Serializing spans here independently would # drop the resource attribute and lose agent identity on the run. trace_file = await manager._save_spans_to_temp_file(session) - with open(trace_file) as f: # noqa: ASYNC230 + with open(trace_file, encoding="utf-8") as f: # noqa: ASYNC230 trace_content = f.read() num_spans = sum(1 for line in trace_content.splitlines() if line.strip()) diff --git a/src/agentevals/cli.py b/src/agentevals/cli.py index 1a9cd89..da26055 100644 --- a/src/agentevals/cli.py +++ b/src/agentevals/cli.py @@ -840,8 +840,8 @@ def migrate_create(name: str, output_dir: str | None) -> None: f"-- Migration {next_version:06d}: {name}\n" "-- Once tagged in a release this file is immutable. Fix bugs by adding a NEW migration.\n\n" ) - up_path.write_text(header) - down_path.write_text(header) + up_path.write_text(header, encoding="utf-8") + down_path.write_text(header, encoding="utf-8") click.echo(f"Created {up_path}") click.echo(f"Created {down_path}") diff --git a/src/agentevals/eval_config_loader.py b/src/agentevals/eval_config_loader.py index 822264b..45518a6 100644 --- a/src/agentevals/eval_config_loader.py +++ b/src/agentevals/eval_config_loader.py @@ -58,7 +58,7 @@ def load_eval_config(path: str | Path) -> EvalRunConfig: if not path.exists(): raise FileNotFoundError(f"Eval config file not found: {path}") - with open(path) as f: + with open(path, encoding="utf-8") as f: data = yaml.safe_load(f) if not isinstance(data, dict): diff --git a/src/agentevals/evaluator/sources.py b/src/agentevals/evaluator/sources.py index 8fe022c..1ebae26 100644 --- a/src/agentevals/evaluator/sources.py +++ b/src/agentevals/evaluator/sources.py @@ -66,7 +66,7 @@ def _read_cache(key: str, ttl: int = _CACHE_TTL_SECONDS) -> list[EvaluatorInfo] if not cache_file.exists(): return None try: - data = json.loads(cache_file.read_text()) + data = json.loads(cache_file.read_text(encoding="utf-8")) if time.time() - data.get("ts", 0) > ttl: return None return [EvaluatorInfo(**item) for item in data["evaluators"]] @@ -83,7 +83,8 @@ def _write_cache(key: str, evaluators: list[EvaluatorInfo]) -> None: "ts": time.time(), "evaluators": [asdict(g) for g in evaluators], } - ) + ), + encoding="utf-8", ) except Exception: pass diff --git a/src/agentevals/evaluator/venv.py b/src/agentevals/evaluator/venv.py index fcbfc88..85232cf 100644 --- a/src/agentevals/evaluator/venv.py +++ b/src/agentevals/evaluator/venv.py @@ -42,7 +42,11 @@ def _venv_key(evaluator_path: Path) -> str: def _is_venv_valid(venv_dir: Path, req_hash: str) -> bool: hash_file = venv_dir / _HASH_FILE - return _venv_python(venv_dir).exists() and hash_file.exists() and hash_file.read_text().strip() == req_hash + return ( + _venv_python(venv_dir).exists() + and hash_file.exists() + and hash_file.read_text(encoding="utf-8").strip() == req_hash + ) def _create_venv(venv_dir: Path, uv: str | None) -> None: @@ -104,7 +108,7 @@ def ensure_venv(evaluator_path: Path) -> Path | None: stderr = exc.stderr.decode() if isinstance(exc.stderr, bytes) else (exc.stderr or "") raise RuntimeError(f"Failed to set up environment for evaluator '{evaluator_path.stem}': {stderr}") from exc - (venv_dir / _HASH_FILE).write_text(req_hash) + (venv_dir / _HASH_FILE).write_text(req_hash, encoding="utf-8") logger.info("Environment ready for '%s'", evaluator_path.stem) return _venv_python(venv_dir) diff --git a/src/agentevals/loader/auto.py b/src/agentevals/loader/auto.py index 3ac3ddb..c50f3df 100644 --- a/src/agentevals/loader/auto.py +++ b/src/agentevals/loader/auto.py @@ -51,7 +51,7 @@ def detect_format(path: str) -> str | None: load can fall back to a default. """ try: - with open(path) as f: + with open(path, encoding="utf-8") as f: if path.lower().endswith(".jsonl"): return OTLP_JSON data = json.load(f) diff --git a/src/agentevals/loader/jaeger.py b/src/agentevals/loader/jaeger.py index cb02338..14a27fd 100644 --- a/src/agentevals/loader/jaeger.py +++ b/src/agentevals/loader/jaeger.py @@ -43,7 +43,7 @@ def format_name(self) -> str: return "jaeger-json" def load(self, source: str) -> list[Trace]: - with open(source) as f: + with open(source, encoding="utf-8") as f: raw = json.load(f) if not isinstance(raw, dict) or "data" not in raw: diff --git a/src/agentevals/loader/otlp.py b/src/agentevals/loader/otlp.py index 8155e02..ed22156 100644 --- a/src/agentevals/loader/otlp.py +++ b/src/agentevals/loader/otlp.py @@ -35,7 +35,7 @@ def format_name(self) -> str: def load(self, source: str) -> list[Trace]: """Load OTLP JSON file or JSONL (one span per line).""" - with open(source) as f: + with open(source, encoding="utf-8") as f: content = f.read().strip() if not content: diff --git a/src/agentevals/mcp_server.py b/src/agentevals/mcp_server.py index 6ed3c4b..4025ea0 100644 --- a/src/agentevals/mcp_server.py +++ b/src/agentevals/mcp_server.py @@ -378,7 +378,7 @@ async def summarize_session(session_id: str) -> SummarizeSessionResponse: raw = await _post("/api/streaming/get-trace", {"session_id": session_id}) - with tempfile.NamedTemporaryFile(mode="w", suffix=".jsonl", delete=False) as f: + with tempfile.NamedTemporaryFile(mode="w", suffix=".jsonl", delete=False, encoding="utf-8") as f: f.write(raw["traceContent"]) tmp_path = f.name diff --git a/src/agentevals/run/fetcher.py b/src/agentevals/run/fetcher.py index 34f8bae..d5c20d8 100644 --- a/src/agentevals/run/fetcher.py +++ b/src/agentevals/run/fetcher.py @@ -37,7 +37,7 @@ class InlineTraceFetcher: async def fetch(self, target: TraceTarget, context: dict) -> list[Trace]: if not target.inline: raise ValueError("InlineTraceFetcher requires target.inline to be set") - with tempfile.NamedTemporaryFile(mode="w", suffix=".json", delete=False) as f: + with tempfile.NamedTemporaryFile(mode="w", suffix=".json", delete=False, encoding="utf-8") as f: json.dump(target.inline, f) path = Path(f.name) try: @@ -61,7 +61,7 @@ async def fetch(self, target: TraceTarget, context: dict) -> list[Trace]: resp = await client.get(url, headers=headers) resp.raise_for_status() payload = resp.json() - with tempfile.NamedTemporaryFile(mode="w", suffix=".json", delete=False) as f: + with tempfile.NamedTemporaryFile(mode="w", suffix=".json", delete=False, encoding="utf-8") as f: json.dump(payload, f) path = Path(f.name) try: diff --git a/src/agentevals/run/sinks.py b/src/agentevals/run/sinks.py index 06fd6c7..db228b3 100644 --- a/src/agentevals/run/sinks.py +++ b/src/agentevals/run/sinks.py @@ -77,7 +77,7 @@ def __init__(self, path: str | Path) -> None: async def _write(self, payload: dict) -> None: async with self._lock: self._path.parent.mkdir(parents=True, exist_ok=True) - with self._path.open("a") as f: # noqa: ASYNC230 + with self._path.open("a", encoding="utf-8") as f: # noqa: ASYNC230 f.write(json.dumps(payload) + "\n") async def emit_partial(self, run_id: UUID, results: list[Result], attempt: int) -> None: diff --git a/src/agentevals/runner.py b/src/agentevals/runner.py index ebca577..eae4066 100644 --- a/src/agentevals/runner.py +++ b/src/agentevals/runner.py @@ -64,7 +64,7 @@ class RunResult(BaseModel): def load_eval_set(path: str) -> EvalSet: - with open(path) as f: + with open(path, encoding="utf-8") as f: data = json.load(f) return EvalSet.model_validate(data) diff --git a/src/agentevals/storage/config.py b/src/agentevals/storage/config.py index fcf5ea4..6947747 100644 --- a/src/agentevals/storage/config.py +++ b/src/agentevals/storage/config.py @@ -88,7 +88,7 @@ def _read_dsn_from_env() -> str | None: file_path = os.environ.get("AGENTEVALS_DATABASE_URL_FILE") if file_path: try: - with open(file_path) as f: + with open(file_path, encoding="utf-8") as f: return f.read().strip() or None except OSError as exc: raise ValueError(f"AGENTEVALS_DATABASE_URL_FILE={file_path!r} is unreadable: {exc}") from exc diff --git a/src/agentevals/streaming/ws_server.py b/src/agentevals/streaming/ws_server.py index f7448fc..966c599 100644 --- a/src/agentevals/streaming/ws_server.py +++ b/src/agentevals/streaming/ws_server.py @@ -664,7 +664,7 @@ async def _save_spans_to_temp_file(self, session: TraceSession) -> Path: # span here; otherwise it's lost on reload and runs can't group by agent. service_name = (session.metadata or {}).get(OTEL_SERVICE_NAME) - with open(temp_file, "w") as f: # noqa: ASYNC230 + with open(temp_file, "w", encoding="utf-8") as f: # noqa: ASYNC230 for span in enriched_spans: span_copy = span.copy() span_copy["traceId"] = session.trace_id @@ -695,7 +695,7 @@ async def _extract_invocations(self, session: TraceSession) -> list[dict]: - modelInfo: Model metadata (model name, tokens, etc.) """ try: - temp_file = tempfile.NamedTemporaryFile(mode="w", suffix=".jsonl", delete=False) + temp_file = tempfile.NamedTemporaryFile(mode="w", suffix=".jsonl", delete=False, encoding="utf-8") has_genai_spans = any( span.get("attributes", []) diff --git a/tests/run/test_sinks.py b/tests/run/test_sinks.py index e0a3bd4..73a6a65 100644 --- a/tests/run/test_sinks.py +++ b/tests/run/test_sinks.py @@ -77,7 +77,7 @@ async def test_emits_partial_and_final(self, tmp_path): await sink.emit_partial(run_id, [_result(run_id)], attempt=1) await sink.emit_final(run_id, {"trace_count": 1}, attempt=1) await sink.emit_error(run_id, "boom", attempt=1) - lines = path.read_text().strip().splitlines() + lines = path.read_text(encoding="utf-8").strip().splitlines() assert len(lines) == 3 partial = json.loads(lines[0]) assert partial["phase"] == "partial" @@ -249,7 +249,7 @@ def _factory(spec: dict) -> FileSink: fanout = build_sinks([{"kind": "plugin_file", "path": str(path)}]) run_id = uuid4() await fanout.emit_final(run_id, {"ok": True}, attempt=1) - assert json.loads(path.read_text().strip())["summary"] == {"ok": True} + assert json.loads(path.read_text(encoding="utf-8").strip())["summary"] == {"ok": True} async def test_programmatic_registration_overrides_builtin(self, isolated_sink_plugins): finals: list[dict] = [] @@ -299,7 +299,7 @@ def file_factory(spec: dict) -> FileSink: ep.load.assert_called_once_with() run_id = uuid4() await fanout.emit_final(run_id, {"via": "ep"}, attempt=1) - assert json.loads(path.read_text().strip())["summary"] == {"via": "ep"} + assert json.loads(path.read_text(encoding="utf-8").strip())["summary"] == {"via": "ep"} async def test_builtin_kind_does_not_load_colliding_entry_point(self, isolated_sink_plugins, capsys): ep = MagicMock() @@ -350,7 +350,7 @@ async def test_missing_plugin_sink_does_not_break_fanout(self, isolated_sink_plu assert any("unknown sink kind 'demo_ndjson'" in r.getMessage() for r in caplog.records) run_id = uuid4() await fanout.emit_final(run_id, {"ok": True}, attempt=1) - assert json.loads(path.read_text().strip())["summary"] == {"ok": True} + assert json.loads(path.read_text(encoding="utf-8").strip())["summary"] == {"ok": True} def _demo_example_sink_installed() -> bool: @@ -372,7 +372,7 @@ async def test_path_dot_writes_default_ndjson(self, tmp_path, monkeypatch): sink = create_demo_sink({"path": "."}) await sink.emit_final(uuid4(), {"x": 1}, attempt=1) out = tmp_path / "agentevals-demo-sink.ndjson" - assert json.loads(out.read_text().strip())["summary"] == {"x": 1} + assert json.loads(out.read_text(encoding="utf-8").strip())["summary"] == {"x": 1} async def test_existing_directory_appends_default_filename(self, tmp_path): d = tmp_path / "outdir" @@ -390,7 +390,7 @@ async def test_directory_with_custom_filename(self, tmp_path): sink = create_demo_sink({"path": str(d), "filename": "runs.jsonl"}) await sink.emit_final(uuid4(), {"n": 2}, attempt=1) - assert json.loads((d / "runs.jsonl").read_text().strip())["summary"] == {"n": 2} + assert json.loads((d / "runs.jsonl").read_text(encoding="utf-8").strip())["summary"] == {"n": 2} class TestSinkFanoutErrorIsolation: diff --git a/tests/storage/test_config.py b/tests/storage/test_config.py index 910b96e..4ae3f27 100644 --- a/tests/storage/test_config.py +++ b/tests/storage/test_config.py @@ -76,7 +76,7 @@ def test_from_env_reads_postgres(self, monkeypatch): def test_from_env_url_file_takes_precedence(self, tmp_path, monkeypatch): dsn_file = tmp_path / "dsn" - dsn_file.write_text("postgresql://from-file/db\n") + dsn_file.write_text("postgresql://from-file/db\n", encoding="utf-8") monkeypatch.setenv("AGENTEVALS_STORAGE_BACKEND", "postgres") monkeypatch.setenv("AGENTEVALS_DATABASE_URL", "postgresql://from-env/db") monkeypatch.setenv("AGENTEVALS_DATABASE_URL_FILE", str(dsn_file)) diff --git a/tests/test_api.py b/tests/test_api.py index 627707d..1d5af56 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -1330,7 +1330,9 @@ def test_download_missing(self): assert resp.status_code == 404 def test_download_success(self): - with tempfile.NamedTemporaryFile(mode="w", suffix=".json", delete=False, dir=tempfile.gettempdir()) as f: + with tempfile.NamedTemporaryFile( + mode="w", suffix=".json", delete=False, dir=tempfile.gettempdir(), encoding="utf-8" + ) as f: f.write('{"test": true}') fname = os.path.basename(f.name) diff --git a/tests/test_cli.py b/tests/test_cli.py index 58b72f9..d0a27ec 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -93,7 +93,7 @@ def fake_server_factory(config): def test_run_preserves_config_file_settings_when_flags_omitted(monkeypatch, tmp_path): trace_file = tmp_path / "trace.json" - trace_file.write_text("{}") + trace_file.write_text("{}", encoding="utf-8") config_file = tmp_path / "eval.yaml" config_file.write_text( textwrap.dedent( @@ -105,7 +105,8 @@ def test_run_preserves_config_file_settings_when_flags_omitted(monkeypatch, tmp_ trace_format: otlp-json output: json """ - ) + ), + encoding="utf-8", ) captured = {} @@ -129,7 +130,7 @@ async def fake_run_evaluation(config): def test_run_merges_explicit_metrics_with_config_file(monkeypatch, tmp_path): trace_file = tmp_path / "trace.json" - trace_file.write_text("{}") + trace_file.write_text("{}", encoding="utf-8") config_file = tmp_path / "eval.yaml" config_file.write_text( textwrap.dedent( @@ -141,7 +142,8 @@ def test_run_merges_explicit_metrics_with_config_file(monkeypatch, tmp_path): type: code path: ./examples/custom_evaluators/tool_call_checker.py """ - ) + ), + encoding="utf-8", ) captured = {} diff --git a/tests/test_eval_config_loader.py b/tests/test_eval_config_loader.py index ad89323..d2c578a 100644 --- a/tests/test_eval_config_loader.py +++ b/tests/test_eval_config_loader.py @@ -26,7 +26,8 @@ def test_load_eval_config_rejects_legacy_keys(tmp_path): trace_format: otlp-json output: json """ - ) + ), + encoding="utf-8", ) with pytest.raises(ValueError, match="Legacy eval config keys are no longer supported"): diff --git a/tests/test_jaeger_loader.py b/tests/test_jaeger_loader.py index bfdc9b7..7d05a8f 100644 --- a/tests/test_jaeger_loader.py +++ b/tests/test_jaeger_loader.py @@ -51,7 +51,7 @@ def test_format_name(self, loader): def test_load_minimal(self, loader, minimal_jaeger_json, tmp_path): path = tmp_path / "test.json" - path.write_text(json.dumps(minimal_jaeger_json)) + path.write_text(json.dumps(minimal_jaeger_json), encoding="utf-8") traces = loader.load(str(path)) assert len(traces) == 1 @@ -63,7 +63,7 @@ def test_load_minimal(self, loader, minimal_jaeger_json, tmp_path): def test_span_tree_structure(self, loader, minimal_jaeger_json, tmp_path): path = tmp_path / "test.json" - path.write_text(json.dumps(minimal_jaeger_json)) + path.write_text(json.dumps(minimal_jaeger_json), encoding="utf-8") trace = loader.load(str(path))[0] @@ -79,7 +79,7 @@ def test_span_tree_structure(self, loader, minimal_jaeger_json, tmp_path): def test_tags_parsed(self, loader, minimal_jaeger_json, tmp_path): path = tmp_path / "test.json" - path.write_text(json.dumps(minimal_jaeger_json)) + path.write_text(json.dumps(minimal_jaeger_json), encoding="utf-8") trace = loader.load(str(path))[0] root = trace.root_spans[0] @@ -89,7 +89,7 @@ def test_tags_parsed(self, loader, minimal_jaeger_json, tmp_path): def test_invalid_format_raises(self, loader, tmp_path): path = tmp_path / "bad.json" - path.write_text(json.dumps({"not_data": []})) + path.write_text(json.dumps({"not_data": []}), encoding="utf-8") with pytest.raises(ValueError, match="Invalid Jaeger JSON format"): loader.load(str(path)) @@ -97,14 +97,14 @@ def test_invalid_format_raises(self, loader, tmp_path): def test_empty_spans_skipped(self, loader, tmp_path): data = {"data": [{"traceID": "empty", "spans": []}]} path = tmp_path / "empty.json" - path.write_text(json.dumps(data)) + path.write_text(json.dumps(data), encoding="utf-8") traces = loader.load(str(path)) assert len(traces) == 0 def test_find_spans_by_operation(self, loader, minimal_jaeger_json, tmp_path): path = tmp_path / "test.json" - path.write_text(json.dumps(minimal_jaeger_json)) + path.write_text(json.dumps(minimal_jaeger_json), encoding="utf-8") trace = loader.load(str(path))[0] found = trace.find_spans_by_operation("child") @@ -113,7 +113,7 @@ def test_find_spans_by_operation(self, loader, minimal_jaeger_json, tmp_path): def test_find_spans_by_tag(self, loader, minimal_jaeger_json, tmp_path): path = tmp_path / "test.json" - path.write_text(json.dumps(minimal_jaeger_json)) + path.write_text(json.dumps(minimal_jaeger_json), encoding="utf-8") trace = loader.load(str(path))[0] found = trace.find_spans_by_tag("key1", "val1") diff --git a/tests/test_loader_auto.py b/tests/test_loader_auto.py index 484e7a0..bcdacd9 100644 --- a/tests/test_loader_auto.py +++ b/tests/test_loader_auto.py @@ -15,7 +15,7 @@ def _write_tmp(content: str, suffix: str = ".json") -> str: - f = tempfile.NamedTemporaryFile(mode="w", suffix=suffix, delete=False) + f = tempfile.NamedTemporaryFile(mode="w", suffix=suffix, delete=False, encoding="utf-8") f.write(content) f.close() return f.name diff --git a/tests/test_openai_eval_backend.py b/tests/test_openai_eval_backend.py index c58f0bd..7ea0f2a 100644 --- a/tests/test_openai_eval_backend.py +++ b/tests/test_openai_eval_backend.py @@ -1,6 +1,7 @@ -import pytest from unittest.mock import MagicMock +import pytest + from agentevals.config import OpenAIEvalDef from agentevals.openai_eval_backend import ( _build_jsonl_items, diff --git a/tests/test_otlp_loader.py b/tests/test_otlp_loader.py index 9e496e3..c3a3428 100644 --- a/tests/test_otlp_loader.py +++ b/tests/test_otlp_loader.py @@ -43,7 +43,7 @@ def test_otlp_loader_jsonl_format(sample_otlp_span): """Test loading JSONL format (one span per line).""" loader = OtlpJsonLoader() - with tempfile.NamedTemporaryFile(mode="w", suffix=".jsonl", delete=False) as f: + with tempfile.NamedTemporaryFile(mode="w", suffix=".jsonl", delete=False, encoding="utf-8") as f: f.write(json.dumps(sample_otlp_span) + "\n") temp_path = f.name @@ -110,7 +110,7 @@ def test_otlp_loader_full_export(): ] } - with tempfile.NamedTemporaryFile(mode="w", suffix=".json", delete=False) as f: + with tempfile.NamedTemporaryFile(mode="w", suffix=".json", delete=False, encoding="utf-8") as f: json.dump(otlp_export, f) temp_path = f.name @@ -169,7 +169,7 @@ def test_otlp_loader_parent_child_relationships(): }, ] - with tempfile.NamedTemporaryFile(mode="w", suffix=".jsonl", delete=False) as f: + with tempfile.NamedTemporaryFile(mode="w", suffix=".jsonl", delete=False, encoding="utf-8") as f: for span in spans: f.write(json.dumps(span) + "\n") temp_path = f.name @@ -200,7 +200,7 @@ def test_otlp_loader_empty_file(): """Test loading an empty file.""" loader = OtlpJsonLoader() - with tempfile.NamedTemporaryFile(mode="w", suffix=".json", delete=False) as f: + with tempfile.NamedTemporaryFile(mode="w", suffix=".json", delete=False, encoding="utf-8") as f: temp_path = f.name try: diff --git a/tests/test_runner.py b/tests/test_runner.py index e537d18..ae56d8a 100644 --- a/tests/test_runner.py +++ b/tests/test_runner.py @@ -273,7 +273,9 @@ def _run(self, match_type, tmp_path): conv_result = convert_traces([_make_tool_trace(["helm_get_release", "helm_list_releases"])])[0] eval_set_path = tmp_path / "eval_set.json" - eval_set_path.write_text(json.dumps(_make_eval_set_json(["helm_list_releases", "helm_get_release"]))) + eval_set_path.write_text( + json.dumps(_make_eval_set_json(["helm_list_releases", "helm_get_release"])), encoding="utf-8" + ) eval_set = load_eval_set(str(eval_set_path)) return asyncio.run( @@ -312,7 +314,9 @@ def test_builtin_custom_evaluator_uses_per_evaluator_match_type(self, tmp_path): conv_result = convert_traces([_make_tool_trace(["helm_get_release", "helm_list_releases"])])[0] eval_set_path = tmp_path / "eval_set.json" - eval_set_path.write_text(json.dumps(_make_eval_set_json(["helm_list_releases", "helm_get_release"]))) + eval_set_path.write_text( + json.dumps(_make_eval_set_json(["helm_list_releases", "helm_get_release"])), encoding="utf-8" + ) eval_set = load_eval_set(str(eval_set_path)) trace_result = asyncio.run(