Source code for autils.devel.script

# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# 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 LICENSE for more details.
#
# Copyright: Red Hat Inc. 2014
# Author: Ruda Moura <rmoura@redhat.com>

"""Module to handle script file creation and management.

This module provides utilities for creating executable script files,
both permanent and temporary, with proper file permissions and
automatic cleanup capabilities.
"""

import os
import shutil
import stat
import tempfile

from autils.file import path as utils_path

#: What is commonly known as "0775" or "u=rwx,g=rwx,o=rx"
DEFAULT_MODE = (
    stat.S_IRUSR
    | stat.S_IWUSR
    | stat.S_IXUSR
    | stat.S_IRGRP
    | stat.S_IWGRP
    | stat.S_IXGRP
    | stat.S_IROTH
    | stat.S_IXOTH
)

#: What is commonly known as "0444" or "u=r,g=r,o=r"
READ_ONLY_MODE = stat.S_IRUSR | stat.S_IRGRP | stat.S_IROTH


[docs] class Script: """Class that represents a script file. This class provides methods to create, save, and remove script files with configurable permissions. It supports context manager protocol for automatic cleanup. """ def __init__(self, path, content, mode=DEFAULT_MODE, open_mode="w"): """Creates an instance of :class:`Script`. When used as a context manager, the script is automatically saved on entry and removed on exit. :param path: The path where the script file will be created. :type path: str :param content: The content to write to the script file. :type content: str or bytes :param mode: File permissions mode. Defaults to 0775 (rwxrwxr-x). :type mode: int :param open_mode: File open mode ('w' for text, 'wb' for binary). :type open_mode: str """ self.path = path self.content = content self.mode = mode self.stored = False self.open_mode = open_mode def __repr__(self): return ( f'{self.__class__.__name__}(path="{self.path}", ' f"stored={self.stored})" ) def __str__(self): return self.path def __enter__(self): self.save() return self def __exit__(self, exc_type, exc_value, exc_traceback): self.remove()
[docs] def save(self): """Store script content to the file system. Creates parent directories if they do not exist and sets the specified file permissions. :return: True if script has been stored successfully. :rtype: bool """ dirname = os.path.dirname(self.path) utils_path.init_dir(dirname) with open(self.path, self.open_mode) as fd: # pylint: disable=W1514 fd.write(self.content) os.chmod(self.path, self.mode) self.stored = True return self.stored
[docs] def remove(self): """Remove script file from the file system. :return: True if the script file was removed, False if it did not exist. :rtype: bool """ if os.path.exists(self.path): os.remove(self.path) self.stored = False return True return False
[docs] class TemporaryScript(Script): """Class that represents a temporary script in an auto-managed directory. The script is created in a temporary directory that is automatically cleaned up when the instance is garbage collected or when used as a context manager. """ # pylint: disable=R0913 def __init__( self, name, content, prefix="avocado_script", mode=DEFAULT_MODE, open_mode="w" ): """Creates an instance of :class:`TemporaryScript`. The script is created in a newly created temporary directory. When used as a context manager, both the script and its directory are automatically removed on exit. The directory is also removed when the object is garbage collected. :param name: The script filename (not the full path). :type name: str :param content: The content to write to the script file. :type content: str or bytes :param prefix: Prefix for the temporary directory name. :type prefix: str :param mode: File permissions mode. Defaults to 0775 (rwxrwxr-x). :type mode: int :param open_mode: File open mode ('w' for text, 'wb' for binary). :type open_mode: str """ tmpdir = tempfile.mkdtemp(prefix=prefix) super().__init__(os.path.join(tmpdir, name), content, mode, open_mode) def __del__(self): self.remove()
[docs] def remove(self): if os.path.exists(os.path.dirname(self.path)): shutil.rmtree(os.path.dirname(self.path)) self.stored = False
[docs] def make_script(path, content, mode=DEFAULT_MODE): """Creates and saves a new script file to the file system. This is a convenience function that creates a Script instance, saves it, and returns the path. :param path: The path where the script file will be created. :type path: str :param content: The content to write to the script file. :type content: str or bytes :param mode: File permissions mode. Defaults to 0775 (rwxrwxr-x). :type mode: int :return: The path to the created script file. :rtype: str """ scpt = Script(path, content, mode=mode) scpt.save() return scpt.path
[docs] def make_temp_script(name, content, prefix="avocado_script", mode=DEFAULT_MODE): """Creates and saves a new temporary script in a temporary directory. This is a convenience function that creates a TemporaryScript instance and saves it. Note that the script's temporary directory will be removed when the TemporaryScript object is garbage collected. :param name: The script filename (not the full path). :type name: str :param content: The content to write to the script file. :type content: str or bytes :param prefix: Prefix for the temporary directory name. :type prefix: str :param mode: File permissions mode. Defaults to 0775 (rwxrwxr-x). :type mode: int :return: The full path to the created script file. :rtype: str """ scpt = TemporaryScript(name, content, prefix=prefix, mode=mode) scpt.save() return scpt.path