Coverage for src/types/files.py: 100.00%

42 statements  

« prev     ^ index     » next       coverage.py v7.15.4, created at 2026-10-18 15:02 +0000

1#!/usr/bin/env python3 

2 

3# Standard libraries 

4from atexit import register, unregister 

5from os import getpid, kill 

6from pathlib import Path 

7from signal import getsignal, SIGINT, signal, SIGTERM 

8from tempfile import _TemporaryFileWrapper as TempFile, NamedTemporaryFile 

9from types import FrameType 

10from typing import Any, Callable, List, Optional 

11 

12# Files class 

13class Files: 

14 

15 # SignalHandler type 

16 SignalHandler = Callable[[int, Optional[FrameType]], Any] | int | None 

17 

18 # Members 

19 registered: bool = False 

20 signal_int: SignalHandler 

21 signal_term: SignalHandler 

22 temps: List[TempFile] = [] # type: ignore[type-arg] 

23 

24 # Register 

25 @staticmethod 

26 def __register() -> None: 

27 

28 # Register cleanup 

29 if not Files.registered: 

30 register(Files.clean) 

31 Files.signal_int = getsignal(SIGINT) 

32 Files.signal_term = getsignal(SIGTERM) 

33 signal(SIGINT, Files.clean) 

34 signal(SIGTERM, Files.clean) 

35 Files.registered = True 

36 

37 # Unregister 

38 @staticmethod 

39 def __unregister() -> None: 

40 

41 # Unregister cleanup 

42 if Files.registered: 

43 unregister(Files.clean) 

44 signal(SIGINT, Files.signal_int) 

45 signal(SIGTERM, Files.signal_term) 

46 Files.registered = False 

47 

48 # Clean 

49 @staticmethod 

50 def clean( 

51 __signalnum: Optional[int] = None, 

52 __frame: Optional[FrameType] = None, 

53 ) -> None: 

54 

55 # Delete all temps 

56 for temp in Files.temps: 

57 temp_file = Path(temp.name) 

58 if temp_file.exists(): 

59 temp_file.unlink() 

60 

61 # Reset temps 

62 Files.temps = [] 

63 

64 # Unregister signals 

65 Files.__unregister() 

66 

67 # Raise signal 

68 if __signalnum: 

69 kill(getpid(), __signalnum) # pragma: no cover 

70 

71 # Temp 

72 @staticmethod 

73 def temp( 

74 path: Optional[str] = None, 

75 mode: str = 'wt', 

76 newline: str = '\n', 

77 prefix: str = '.tmp.', 

78 ) -> TempFile: # type: ignore[type-arg] 

79 

80 # Create temporary file, pylint: disable=consider-using-with 

81 temp_file = NamedTemporaryFile( 

82 delete=False, 

83 dir=path, 

84 mode=mode, 

85 newline=newline, 

86 prefix=prefix, 

87 ) 

88 

89 # Register temporary file 

90 Files.temps += [temp_file] 

91 

92 # Register signals 

93 Files.__register() 

94 

95 # Result 

96 return temp_file