Skip to content

Commit 9677b49

Browse files
committed
Fix FileAggregator resource leak
Track opened files in InterpreterState and close them in process_prog finally block. Signed-off-by: Louis Mandel <lmandel@us.ibm.com>
1 parent 019e251 commit 9677b49

File tree

2 files changed

+18
-5
lines changed

2 files changed

+18
-5
lines changed

src/pdl/pdl_interpreter.py

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -280,10 +280,20 @@ def process_prog(
280280
{"stdlib": stdlib_dict, "pdl_usage": state.llm_usage}
281281
)
282282

283-
result, document, final_scope, trace = process_block(
284-
state, stdlib_scope, block=prog.root, loc=loc
285-
)
286-
return result, document, final_scope, trace
283+
try:
284+
result, document, final_scope, trace = process_block(
285+
state, stdlib_scope, block=prog.root, loc=loc
286+
)
287+
return result, document, final_scope, trace
288+
finally:
289+
# Close all opened files
290+
for fp in state.opened_files:
291+
try:
292+
if not fp.closed:
293+
fp.close()
294+
except Exception:
295+
# Ignore errors during cleanup
296+
pass # nosec B110
287297

288298

289299
def process_block(
@@ -2657,6 +2667,7 @@ def process_aggregator(
26572667
fp = open( # pylint: disable=consider-using-with
26582668
file, mode=mode, encoding=encoding
26592669
)
2670+
state.opened_files.append(fp) # Track for cleanup
26602671
aggregator = FileAggregator(fp, prefix=prefix, suffix=suffix, flush=flush)
26612672
case _:
26622673
assert False, "Unexpected aggregator"

src/pdl/pdl_interpreter_state.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
from asyncio import AbstractEventLoop
22
from pathlib import Path
3-
from typing import Any
3+
from typing import IO, Any
44

55
from pydantic import BaseModel, ConfigDict, Field
66

@@ -56,6 +56,8 @@ class InterpreterState(BaseModel):
5656
"""Dictionary that associate runtime block ids with their values to be able to replay an execution."""
5757
llm_usage: PdlUsage = PdlUsage()
5858
"""Data structure where to accumulate LLMs usage."""
59+
opened_files: list[IO[Any]] = Field(default_factory=list)
60+
"""List of file handles opened during execution that need to be closed."""
5961

6062
def with_yield_result(self: "InterpreterState", b: bool) -> "InterpreterState":
6163
return self.model_copy(update={"yield_result": b})

0 commit comments

Comments
 (0)