25
25
import os .path as osp
26
26
import re
27
27
import tempfile
28
+ import weakref
28
29
29
30
from traits .api import Dict , String , Undefined
30
31
@@ -493,17 +494,60 @@ def raise_for_status(self, status, execution_id=None):
493
494
494
495
495
496
class 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
+
496
513
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
+ """
497
523
fd , path = tempfile .mkstemp (** kwargs )
498
524
instance = super ().__new__ (cls , path )
499
525
instance .path = path
500
526
instance .fd = fd
501
527
return instance
502
528
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
506
543
544
+ try :
545
+
546
+ if os .path .exists (self .path ):
547
+ os .remove (self .path )
548
+
549
+ except OSError :
550
+ pass
507
551
508
552
def database_factory (database_location ):
509
553
"""
@@ -516,7 +560,9 @@ def database_factory(database_location):
516
560
engine_directory = None
517
561
518
562
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
+ )
520
566
match = _populsedb_url_re .match (database_location )
521
567
if match :
522
568
path = match .groups (2 )
@@ -533,6 +579,11 @@ def database_factory(database_location):
533
579
raise ValueError ("Invalid database location: %s" % database_location )
534
580
535
581
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
+
536
587
if engine_directory :
537
588
engine .set_named_directory ("capsul_engine" , engine_directory )
538
589
return engine
0 commit comments