1313# 
1414# You should have received a copy of the GNU General Public License 
1515# along with this program.  If not, see <https://www.gnu.org/licenses/>. 
16+ """I/O helper functions that handle a slow disk (or network disk) gracefully. 
17+ """ 
1618import  contextlib 
1719import  errno 
1820import  io 
2628
2729
2830MAIN_LOCK  =  threading .RLock ()
31+ """Lock for coordinating the wait on start when the (network) disk is not 
32+ ready yet. Network disks can take a bit to get ready after a container is 
33+ started.""" 
2934STALE_FILE_RETRIES : list [float ] =  [0.1 , 0.2 , 0.5 , 0.8 , 1 , 1.2 , 1.5 , 2 , 3 , 5 ]
35+ """Wait times for retrying reads on stale files.""" 
3036TMP_POSTFIX  =  ".~tmp" 
37+ """Postfix for temporary files.""" 
3138
3239
3340def  when_ready (fun : Callable [[], None ]) ->  None :
41+     """ 
42+     Executes an I/O operation, retrying if the disk is not ready. After 120 
43+     retries (~2min) the function gives up and lets the error go through. 
44+ 
45+     Args: 
46+         fun (Callable[[], None]): The I/O operation. 
47+     """ 
3448    with  MAIN_LOCK :
3549        counter  =  0 
3650        while  True :
@@ -46,6 +60,13 @@ def when_ready(fun: Callable[[], None]) -> None:
4660
4761
4862def  fastrename (src : str , dst : str ) ->  None :
63+     """ 
64+     Moves a file or folder. Source and destination cannot be the same. 
65+ 
66+     Args: 
67+         src (str): The source file or folder. 
68+         dst (str): The destination file or folder. 
69+     """ 
4970    src  =  os .path .abspath (src )
5071    dst  =  os .path .abspath (dst )
5172    if  src  ==  dst :
@@ -71,10 +92,26 @@ def fastrename(src: str, dst: str) -> None:
7192
7293
7394def  copy_file (from_file : str , to_file : str ) ->  None :
95+     """ 
96+     Copies a file to a new destination. 
97+ 
98+     Args: 
99+         from_file (str): The source file. 
100+         to_file (str): The destination file. 
101+     """ 
74102    shutil .copy (from_file , to_file )
75103
76104
77105def  normalize_folder (folder : str ) ->  str :
106+     """ 
107+     Makes the path absolute and ensures that the folder exists. 
108+ 
109+     Args: 
110+         folder (str): The folder. 
111+ 
112+     Returns: 
113+         str: The absolute path. 
114+     """ 
78115    res  =  os .path .abspath (folder )
79116    when_ready (lambda : os .makedirs (res , mode = 0o777 , exist_ok = True ))
80117    if  not  os .path .isdir (res ):
@@ -83,16 +120,44 @@ def normalize_folder(folder: str) -> str:
83120
84121
85122def  normalize_file (fname : str ) ->  str :
123+     """ 
124+     Makes the path absolute and ensures that the parent folder exists. 
125+ 
126+     Args: 
127+         fname (str): The file. 
128+ 
129+     Returns: 
130+         str: The absolute path. 
131+     """ 
86132    res  =  os .path .abspath (fname )
87133    normalize_folder (os .path .dirname (res ))
88134    return  res 
89135
90136
91137def  get_mode (base : str , text : bool ) ->  str :
138+     """ 
139+     Creates a mode string for the `open` function. 
140+ 
141+     Args: 
142+         base (str): The base mode string. 
143+         text (bool): Whether it is a text file. 
144+ 
145+     Returns: 
146+         str: The mode string. 
147+     """ 
92148    return  f"{ base } { ''  if  text  else  'b' }  " 
93149
94150
95151def  is_empty_file (fin : IO [Any ]) ->  bool :
152+     """ 
153+     Cheecks whether the given file is empty. 
154+ 
155+     Args: 
156+         fin (IO[Any]): The file handle. 
157+ 
158+     Returns: 
159+         bool: True, if the file is empty. 
160+     """ 
96161    pos  =  fin .seek (0 , io .SEEK_CUR )
97162    size  =  fin .seek (0 , io .SEEK_END ) -  pos 
98163    fin .seek (pos , io .SEEK_SET )
@@ -110,6 +175,15 @@ def ensure_folder(folder: None) -> None:
110175
111176
112177def  ensure_folder (folder : str  |  None ) ->  str  |  None :
178+     """ 
179+     Ensures that the given folder exists. 
180+ 
181+     Args: 
182+         folder (str | None): The folder name or None. 
183+ 
184+     Returns: 
185+         str | None: The folder name or None. 
186+     """ 
113187    if  folder  is  not   None  and  not  os .path .exists (folder ):
114188        a_folder : str  =  folder 
115189        when_ready (lambda : os .makedirs (a_folder , mode = 0o777 , exist_ok = True ))
0 commit comments