2525import os .path as osp
2626import re
2727import tempfile
28+ import weakref
2829
2930from traits .api import Dict , String , Undefined
3031
@@ -493,17 +494,60 @@ def raise_for_status(self, status, execution_id=None):
493494
494495
495496class AutoDeleteFileName (str ):
497+ """
498+ A temporary file name that automatically deletes the file when `cleanup()`
499+ is called.
500+
501+ This class creates a temporary file using `tempfile.mkstemp()`, stores its
502+ file descriptor and path, and behaves like a string (inherits from `str`).
503+ It is useful when you need to pass a file path around but also want to
504+ ensure the file is properly deleted later.
505+
506+ :param **kwargs: Keyword arguments passed to `tempfile.mkstemp()` such as
507+ `prefix`,`suffix`, or `dir`.
508+
509+ :returns (AutoDeleteFileName): An instance that acts like a string path
510+ but can clean up its underlying file.
511+ """
512+
496513 def __new__ (cls , ** kwargs ):
514+ """
515+ Create a temporary file and return an AutoDeleteFileName instance with
516+ the file path.
517+
518+ :param **kwargs: Keyword arguments passed to `tempfile.mkstemp()`.
519+
520+ :returns (AutoDeleteFileName): The string path of the created
521+ temporary file.
522+ """
497523 fd , path = tempfile .mkstemp (** kwargs )
498524 instance = super ().__new__ (cls , path )
499525 instance .path = path
500526 instance .fd = fd
501527 return instance
502528
503- def __del__ (self ):
504- os .close (self .fd )
505- os .remove (self .path )
529+ def cleanup (self ):
530+ """
531+ Close and delete the temporary file if it exists.
532+
533+ This method ensures the file descriptor is closed and the file is
534+ removed from the filesystem. Errors during cleanup are silently
535+ ignored.
536+ """
537+
538+ try :
539+ os .close (self .fd )
540+
541+ except OSError :
542+ pass
506543
544+ try :
545+
546+ if os .path .exists (self .path ):
547+ os .remove (self .path )
548+
549+ except OSError :
550+ pass
507551
508552def database_factory (database_location ):
509553 """
@@ -516,7 +560,9 @@ def database_factory(database_location):
516560 engine_directory = None
517561
518562 if database_location is None :
519- database_location = AutoDeleteFileName (prefix = "populse_db_" , suffix = ".sqlite" )
563+ database_location = AutoDeleteFileName (
564+ prefix = "populse_db_" , suffix = ".sqlite"
565+ )
520566 match = _populsedb_url_re .match (database_location )
521567 if match :
522568 path = match .groups (2 )
@@ -533,6 +579,11 @@ def database_factory(database_location):
533579 raise ValueError ("Invalid database location: %s" % database_location )
534580
535581 engine = PopulseDBEngine (populse_db )
582+
583+ if isinstance (database_location , AutoDeleteFileName ):
584+ # attach cleanup so it deletes when engine is done
585+ weakref .finalize (engine , database_location .cleanup )
586+
536587 if engine_directory :
537588 engine .set_named_directory ("capsul_engine" , engine_directory )
538589 return engine
0 commit comments