1+ import logging
12import os
23import time
34import traceback
45from pathlib import Path
56
67from codeclash .utils .aws import get_aws_metadata
78from codeclash .utils .environment import create_file_in_container
8- from codeclash .utils .log import add_root_file_handler , get_logger
9+ from codeclash .utils .log import add_root_file_handler , get_logger , remove_file_handler
910
1011
1112class AbstractTournament :
@@ -22,7 +23,7 @@ def __init__(self, config: dict, *, name: str, output_dir: Path, **kwargs):
2223 "aws" : get_aws_metadata (),
2324 }
2425 self .logger = get_logger (self .name , log_path = self .local_output_dir / "tournament.log" , emoji = "🏆" )
25- add_root_file_handler (self .local_output_dir / "everything.log" )
26+ self . _root_file_handler = add_root_file_handler (self .local_output_dir / "everything.log" )
2627
2728 @property
2829 def local_output_dir (self ) -> Path :
@@ -33,6 +34,17 @@ def local_output_dir(self) -> Path:
3334 def get_metadata (self ) -> dict :
3435 return self ._metadata
3536
37+ def cleanup_handlers (self ) -> None :
38+ """Close and remove file handlers to prevent file descriptor leaks."""
39+ # Close the root file handler
40+ remove_file_handler (logging .getLogger (), self ._root_file_handler )
41+
42+ # Close logger's file handlers
43+ for handler in self .logger .handlers [:]: # Copy list to avoid modification during iteration
44+ if isinstance (handler , logging .FileHandler ):
45+ self .logger .removeHandler (handler )
46+ handler .close ()
47+
3648 def _copy_game_log_to_agent (self , agent , round_num : int , log_output : str , dest_path : str = None ) -> None :
3749 """Copy round log to agent environment."""
3850 try :
0 commit comments