Source code for gemseo.utils.file_path_manager

# Copyright 2021 IRT Saint Exupéry, https://www.irt-saintexupery.com
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License version 3 as published by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with this program; if not, write to the Free Software Foundation,
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
"""Services for handling file paths."""
from __future__ import annotations

import re
from collections import namedtuple
from enum import Enum
from pathlib import Path
from re import findall

from gemseo.utils.string_tools import MultiLineString

FileDefinition = namedtuple("FileDefinition", ["name", "extension"])


[docs]class FileType(Enum): """The type of a file, defined by its default name and format.""" FIGURE = FileDefinition("figure", "png") SCHEMA = FileDefinition("schema", "json") TEXT = FileDefinition("document", "txt") WEBPAGE = FileDefinition("page", "html")
[docs]class FilePathManager: """A factory of file paths for a given type of file and with default settings.""" def __init__( self, file_type: FileType, default_name: str = None, default_directory: Path | None = None, default_extension: str | None = None, ) -> None: """ Args: file_type: The type of file, defined by its default file name and format; select a file type by iterating over ``FileType``. default_name: The default file name. If None, use the default file name related to the given type of file. default_directory: The default directory path. If None, use the current working directory. default_extension: The default extension. If None, use the default extension related to the given type of file. """ if default_name is None: self.__default_name = file_type.value.name else: self.__default_name = default_name self.__default_directory = Path(default_directory or Path.cwd()) if default_extension is None: self.__default_extension = file_type.value.extension else: self.__default_extension = default_extension self.__file_type = file_type.name def __str__(self) -> str: string = MultiLineString() string.add(self.__class__.__name__) string.indent() string.add("File type: {}", self.__file_type) string.add("Default file name: {}", self.__default_name) string.add("Default file extension: {}", self.__default_extension) string.add("Default directory: {}", self.__default_directory) return str(string)
[docs] def create_file_path( self, file_path: str | Path | None = None, directory_path: str | Path | None = None, file_name: str | None = None, file_extension: str | None = None, ) -> Path: """Make a file path from a directory path, a file name and a file extension. Args: file_path: The path of the file to be returned. If None, create a file path from ``directory_path``, ``file_name`` and ``file_extension``. directory_path: The path of the directory. If None, use the default directory path. file_name: The file name to be used. If None, use the default file name. file_extension: A file extension, e.g. 'png', 'pdf', 'svg', ... If None, use the default file extension. Returns: The file path. """ suffix = f".{file_extension or self.__default_extension}" if file_path is not None: file_path = Path(file_path) if not file_path.suffix: file_path = file_path.with_suffix(suffix) return file_path file_name = file_name or self.__default_name if directory_path is None: directory_path = self.__default_directory else: directory_path = Path(directory_path) return (directory_path / file_name).with_suffix(suffix)
[docs] @staticmethod def to_snake_case( message: str, ) -> str: """Snake case a string. That means: 1. Split the message. 2. Lowercase the resulting elements. ``-`` and `` `` are replaced with ``_``. Args: message: The message to be snake-cased. Returns: The snake-cased message. """ message = message.replace("-", "_").replace(" ", "_") message = "_".join( [elem.lower() for elem in findall("[A-Z][^A-Z]*", message)] or [message] ) return re.sub("_+", "_", message)
[docs] @classmethod def add_suffix( cls, file_path: Path, suffix: str, ) -> Path: """Add a suffix to an existing file path between the filename and the extension. E.g. `directory/filename_suffix.pdf`. Args: file_path: The file path to be suffixed. suffix: The suffix to be added to the file path. Returns: The directory path, the file name and the file extension obtained from the file path. """ extension = file_path.suffix file_name = f"{str(file_path.stem)}_{suffix}" directory_path = file_path.parent return (directory_path / file_name).with_suffix(extension)