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
« prev ^ index » next coverage.py v7.15.4, created at 2026-10-18 15:02 +0000
1#!/usr/bin/env python3
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
12# Files class
13class Files:
15 # SignalHandler type
16 SignalHandler = Callable[[int, Optional[FrameType]], Any] | int | None
18 # Members
19 registered: bool = False
20 signal_int: SignalHandler
21 signal_term: SignalHandler
22 temps: List[TempFile] = [] # type: ignore[type-arg]
24 # Register
25 @staticmethod
26 def __register() -> None:
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
37 # Unregister
38 @staticmethod
39 def __unregister() -> None:
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
48 # Clean
49 @staticmethod
50 def clean(
51 __signalnum: Optional[int] = None,
52 __frame: Optional[FrameType] = None,
53 ) -> None:
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()
61 # Reset temps
62 Files.temps = []
64 # Unregister signals
65 Files.__unregister()
67 # Raise signal
68 if __signalnum:
69 kill(getpid(), __signalnum) # pragma: no cover
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]
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 )
89 # Register temporary file
90 Files.temps += [temp_file]
92 # Register signals
93 Files.__register()
95 # Result
96 return temp_file