import glob
import logging
import os
import shutil
import tempfile
import uuid
from os import path
logger = logging.getLogger(__name__)
[docs]
class TemporaryFile:
def __enter__(self):
self.dir = tempfile.mkdtemp()
self.name = str(uuid.uuid4())
self.fullpath = path.join(self.dir, self.name)
return self # <--- return the object itself
def __exit__(self, exc_type, exc_value, traceback):
shutil.rmtree(self.dir)
def __str__(self):
return self.fullpath
[docs]
def remove(self):
shutil.rmtree(self.dir)
[docs]
def is_valid_filename(filename: str) -> tuple[bool, str]:
"""Verifies is a string is a valid filename"""
# Check for zero length or None
if not filename or len(filename) == 0:
return False, 'Name is empty'
# Define invalid characters for file names (common for Windows, Linux, and macOS)
invalid_chars = '<>:"/\\|?*'
if any(char in invalid_chars for char in filename):
return False, f'Name contains one invalid char: {invalid_chars}'
# Check for system reserved names in Windows
if os.name == 'nt': # nt means Windows
reserved_names = {
"CON",
"PRN",
"AUX",
"NUL",
"COM1",
"COM2",
"COM3",
"COM4",
"COM5",
"COM6",
"COM7",
"COM8",
"COM9",
"LPT1",
"LPT2",
"LPT3",
"LPT4",
"LPT5",
"LPT6",
"LPT7",
"LPT8",
"LPT9",
}
if filename.upper() in reserved_names:
return False, f'Name is a reserved name: {reserved_names}'
# Check filename length (common limit is 255 for many file systems, but this is OS dependent)
if len(filename) > 255:
return False, f'The length of the filename exceeds 255: {len(filename)}'
return True, ''
[docs]
def create_backup(filename: str, rename: bool = True) -> str:
"""If the file exist, we create a backup copy.
:param filename: name of the file
:param rename: if True, the file is renamed. If False, a copy is made.
"""
original_base_name, original_extension = os.path.splitext(filename)
# Check if the file already exists
if os.path.exists(filename):
# Initialize a counter for potential file name conflicts
counter = 1
# Generate a new file name by appending a number
# Keep incrementing the number if the file already exists
while True:
new_name = f'{original_base_name}_{counter}{original_extension}'
if not os.path.exists(new_name):
break # Exit loop if the file name does not exist
counter += 1 # Increment the counter and try again
# Rename the old file to the new file name
if rename:
os.rename(filename, new_name)
logger.info(f'File {filename} has been renamed {new_name}')
else:
shutil.copy(filename, new_name)
logger.info(f'File {filename} has been copied as {new_name}')
return new_name
logger.info(f'File {filename} does not exist. No backup has been generated')
[docs]
def files_of_type(extension: str, name: str, all_files: bool = False) -> list[str]:
"""Identify the list of files with a given extension in the
local directory
:param extension: extension of the requested files (without
the dot): 'yaml', or 'html'
:param name: filename, without extension
:param all_files: if all_files is False, only files containing
the name of the model are identified. If all_files is
True, all files with the requested extension are
identified.
:return: list of files with the requested extension.
"""
if all_files:
pattern = f"*.{extension}"
return glob.glob(pattern)
pattern1 = f"{name}.{extension}"
pattern2 = f"{name}~*.{extension}"
files = glob.glob(pattern1) + glob.glob(pattern2)
return files