1717import pathlib
1818import shutil
1919import tempfile
20+ import typing as t
21+ from collections .abc import Iterator
2022
2123from . import timezone
2224from .lang import type_check
25+ from .typing import FilePath , Self
2326
2427# If True, tries to make everything (dirs, files) group-writable.
2528# Otherwise, tries to make everything only readable and writable by the user.
@@ -45,7 +48,7 @@ class Folder:
4548 to os.path.abspath or normpath are quite slow).
4649 """
4750
48- def __init__ (self , abspath , folder_limit = None ):
51+ def __init__ (self , abspath : FilePath , folder_limit : FilePath | None = None ):
4952 """Construct a new instance."""
5053 abspath = os .path .abspath (abspath )
5154 if folder_limit is None :
@@ -64,22 +67,22 @@ def __init__(self, abspath, folder_limit=None):
6467 self ._folder_limit = folder_limit
6568
6669 @property
67- def mode_dir (self ):
70+ def mode_dir (self ) -> int :
6871 """Return the mode with which the folders should be created"""
6972 if GROUP_WRITABLE :
7073 return 0o770
7174
7275 return 0o700
7376
7477 @property
75- def mode_file (self ):
78+ def mode_file (self ) -> int :
7679 """Return the mode with which the files should be created"""
7780 if GROUP_WRITABLE :
7881 return 0o660
7982
8083 return 0o600
8184
82- def get_subfolder (self , subfolder , create = False , reset_limit = False ):
85+ def get_subfolder (self , subfolder : FilePath , create = False , reset_limit = False ) -> Folder :
8386 """Return a Folder object pointing to a subfolder.
8487
8588 :param subfolder: a string with the relative path of the subfolder,
@@ -110,7 +113,7 @@ def get_subfolder(self, subfolder, create=False, reset_limit=False):
110113
111114 return new_folder
112115
113- def get_content_list (self , pattern = '*' , only_paths = True ):
116+ def get_content_list (self , pattern : str = '*' , only_paths : bool = True ) -> list :
114117 """Return a list of files (and subfolders) in the folder, matching a given pattern.
115118
116119 Example: If you want to exclude files starting with a dot, you can
@@ -134,7 +137,7 @@ def get_content_list(self, pattern='*', only_paths=True):
134137
135138 return [(fname , not os .path .isdir (os .path .join (self .abspath , fname ))) for fname in file_list ]
136139
137- def create_symlink (self , src , name ) :
140+ def create_symlink (self , src : FilePath , name : FilePath ) -> None :
138141 """Create a symlink inside the folder to the location 'src'.
139142
140143 :param src: the location to which the symlink must point. Can be
@@ -148,7 +151,7 @@ def create_symlink(self, src, name):
148151
149152 # For symlinks, permissions should not be set
150153
151- def insert_path (self , src , dest_name = None , overwrite = True ):
154+ def insert_path (self , src : FilePath , dest_name : FilePath | None = None , overwrite : bool = True ) -> FilePath :
152155 """Copy a file to the folder.
153156
154157 :param src: the source filename to copy
@@ -205,7 +208,9 @@ def insert_path(self, src, dest_name=None, overwrite=True):
205208
206209 return dest_abs_path
207210
208- def create_file_from_filelike (self , filelike , filename , mode = 'wb' , encoding = None ):
211+ def create_file_from_filelike (
212+ self , filelike : t .IO [t .AnyStr ], filename : FilePath , mode : str = 'wb' , encoding : str | None = None
213+ ) -> FilePath :
209214 """Create a file with the given filename from a filelike object.
210215
211216 :param filelike: a filelike object whose contents to copy
@@ -227,7 +232,7 @@ def create_file_from_filelike(self, filelike, filename, mode='wb', encoding=None
227232
228233 return filepath
229234
230- def remove_path (self , filename ) :
235+ def remove_path (self , filename : FilePath ) -> None :
231236 """Remove a file or folder from the folder.
232237
233238 :param filename: the relative path name to remove
@@ -241,7 +246,7 @@ def remove_path(self, filename):
241246 else :
242247 os .remove (dest_abs_path )
243248
244- def get_abs_path (self , relpath , check_existence = False ):
249+ def get_abs_path (self , relpath : FilePath , check_existence : bool = False ) -> FilePath :
245250 """Return an absolute path for a file or folder in this folder.
246251
247252 The advantage of using this method is that it checks that filename
@@ -268,7 +273,9 @@ def get_abs_path(self, relpath, check_existence=False):
268273 return dest_abs_path
269274
270275 @contextlib .contextmanager
271- def open (self , name , mode = 'r' , encoding = 'utf8' , check_existence = False ):
276+ def open (
277+ self , name : FilePath , mode : str = 'r' , encoding : str | None = 'utf8' , check_existence : bool = False
278+ ) -> Iterator [t .Any ]:
272279 """Open a file in the current folder and return the corresponding file object.
273280
274281 :param check_existence: if False, just return the file path.
@@ -282,32 +289,32 @@ def open(self, name, mode='r', encoding='utf8', check_existence=False):
282289 yield handle
283290
284291 @property
285- def abspath (self ):
292+ def abspath (self ) -> FilePath :
286293 """The absolute path of the folder."""
287294 return self ._abspath
288295
289296 @property
290- def folder_limit (self ):
297+ def folder_limit (self ) -> FilePath :
291298 """The folder limit that cannot be crossed when creating files and folders."""
292299 return self ._folder_limit
293300
294- def exists (self ):
301+ def exists (self ) -> bool :
295302 """Return True if the folder exists, False otherwise."""
296303 return os .path .exists (self .abspath )
297304
298- def isfile (self , relpath ) :
305+ def isfile (self , relpath : FilePath ) -> bool :
299306 """Return True if 'relpath' exists inside the folder and is a file,
300307 False otherwise.
301308 """
302309 return os .path .isfile (os .path .join (self .abspath , relpath ))
303310
304- def isdir (self , relpath ) :
311+ def isdir (self , relpath : FilePath ) -> bool :
305312 """Return True if 'relpath' exists inside the folder and is a directory,
306313 False otherwise.
307314 """
308315 return os .path .isdir (os .path .join (self .abspath , relpath ))
309316
310- def erase (self , create_empty_folder = False ):
317+ def erase (self , create_empty_folder : bool = False ) -> None :
311318 """Erases the folder. Should be called only in very specific cases,
312319 in general folder should not be erased!
313320
@@ -321,7 +328,7 @@ def erase(self, create_empty_folder=False):
321328 if create_empty_folder :
322329 self .create ()
323330
324- def create (self ):
331+ def create (self ) -> None :
325332 """Creates the folder, if it does not exist on the disk yet.
326333
327334 It will also create top directories, if absent.
@@ -331,7 +338,7 @@ def create(self):
331338 """
332339 os .makedirs (self .abspath , mode = self .mode_dir , exist_ok = True )
333340
334- def replace_with_folder (self , srcdir , move = False , overwrite = False ):
341+ def replace_with_folder (self , srcdir : FilePath , move : bool = False , overwrite : bool = False ) -> None :
335342 """This routine copies or moves the source folder 'srcdir' to the local folder pointed to by this Folder.
336343
337344 :param srcdir: the source folder on the disk; this must be an absolute path
@@ -399,11 +406,11 @@ def __init__(self, filepath: pathlib.Path | None = None):
399406
400407 super ().__init__ (abspath = tempfile .mkdtemp (dir = filepath ))
401408
402- def __enter__ (self ):
409+ def __enter__ (self ) -> Self :
403410 """Enter a context and return self."""
404411 return self
405412
406- def __exit__ (self , exc_type , exc_value , traceback ):
413+ def __exit__ (self , exc_type , exc_value , traceback ) -> None :
407414 """Erase the temporary directory created in the constructor."""
408415 self .erase ()
409416
@@ -416,9 +423,7 @@ class SubmitTestFolder(Folder):
416423 not overwrite already existing created test folders.
417424 """
418425
419- _sub_folder = None
420-
421- def __init__ (self , basepath = CALC_JOB_DRY_RUN_BASE_PATH ):
426+ def __init__ (self , basepath : FilePath = CALC_JOB_DRY_RUN_BASE_PATH ):
422427 """Construct and create the sandbox folder.
423428
424429 The directory will be created in the current working directory with the name given by `basepath`.
@@ -451,9 +456,9 @@ def __init__(self, basepath=CALC_JOB_DRY_RUN_BASE_PATH):
451456
452457 self ._sub_folder = self .get_subfolder (os .path .relpath (subfolder_path , self .abspath ), reset_limit = True )
453458
454- def __enter__ (self ):
459+ def __enter__ (self ) -> Folder :
455460 """Return the sub folder that should be Called when entering in the with statement."""
456461 return self ._sub_folder
457462
458- def __exit__ (self , exc_type , exc_value , traceback ):
463+ def __exit__ (self , exc_type , exc_value , traceback ) -> None :
459464 """When context manager is exited, do not delete the folder."""
0 commit comments