Program Listing for File filetools.py

Return to documentation for file (pymdtools/filetools.py)

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# -----------------------------------------------------------------------------
#
# Copyright (c) 2018 Florent TOURNOIS
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
#
# -----------------------------------------------------------------------------

# -----------------------------------------------------------------------------
# standard object to wrap file and access easily to the filename
#
# -----------------------------------------------------------------------------

import logging
import sys
import os
import os.path

from . import common

# -----------------------------------------------------------------------------
# get template
#
# @param filename the filename
# @param start_folder the folder to search the template
# @return the content
# -----------------------------------------------------------------------------
def get_template_file(filename, start_folder=None):
    if start_folder is None:
        start_folder = os.path.split(__get_this_filename())[0]

    local_template_folder = common.check_folder(
        os.path.join(start_folder, "template"))

    result = common.get_file_content(os.path.join(local_template_folder,
                                                  filename))

    return result


# -----------------------------------------------------------------------------
# get template folder file list
#
# @param folder the folder to scan
# @return the list of template files in a subfolder of template
# -----------------------------------------------------------------------------
def get_template_files_in_folder(folder):
    local_template_folder = common.check_folder(os.path.join(os.path.split(
        __get_this_filename())[0], os.path.join("template", folder)))

    result = []
    for filename in os.listdir(local_template_folder):
        if os.path.isfile(os.path.join(local_template_folder, filename)):
            result.append(os.path.join(folder, filename))

    return result


# -----------------------------------------------------------------------------
# Object for file name.
# Provide manipulation on filename
# Can be a object base for other purpose.
# -----------------------------------------------------------------------------
class FileName:

    # -------------------------------------------------------------------------
    # Initialize the object with the filename
    #
    # @param filename the filename to deal with
    # -------------------------------------------------------------------------
    def __init__(self, filename=None):
        # set the first value
        self.__full_filename = None

        # set the filenames
        if filename is not None:
            self.full_filename = filename

    # -------------------------------------------------------------------------
    # the full filename with the complet path
    # @return the value
    # -------------------------------------------------------------------------
    @property
    def full_filename(self):
        return self.__full_filename

    # -------------------------------------------------------------------------
    # the full filename with the complet path
    # @param value The value to set
    # -------------------------------------------------------------------------
    @full_filename.setter
    def full_filename(self, value):
        if value is None:
            self.__full_filename = None
            return

        self.__full_filename = common.set_correct_path(value)

    # -------------------------------------------------------------------------
    # the filename (only the last part of the full filename)
    # @return the value
    # -------------------------------------------------------------------------
    @property
    def filename(self):
        return os.path.split(self.__full_filename)[1]

    # -------------------------------------------------------------------------
    # the filename (only the last part of the full filename)
    # @param value The value to set
    # -------------------------------------------------------------------------
    @filename.setter
    def filename(self, value):
        if value is None:
            self.__full_filename = None
            return

        value = common.get_valid_filename(value)

        if self.__full_filename is None:
            self.__full_filename = value
        else:
            self.__full_filename = os.path.split(self.__full_filename)[0]
            self.__full_filename = os.path.join(self.__full_filename, value)

        self.__full_filename = common.set_correct_path(self.__full_filename)

    # -------------------------------------------------------------------------
    # the path to the filename (only the first part of the full filename)
    # @return the value
    # -------------------------------------------------------------------------
    @property
    def filename_path(self):
        if self.__full_filename is None:
            return None
        return os.path.split(self.__full_filename)[0]

    # -------------------------------------------------------------------------
    # the path to the filename (only the first part of the full filename)
    # @param value The value to set
    # -------------------------------------------------------------------------
    @filename_path.setter
    def filename_path(self, value):
        if value is None:
            logging.error('Can not set an empty path')
            raise Exception('Can not set an empty path')

        value = common.set_correct_path(value)
        self.__full_filename = os.path.join(value,
                                            os.path.split(self.filename)[1])

    # -------------------------------------------------------------------------
    # the extension of the filename
    # @return the value
    # -------------------------------------------------------------------------
    @property
    def filename_ext(self):
        if self.__full_filename is None:
            return None
        return os.path.splitext(self.__full_filename)[1]

    # -------------------------------------------------------------------------
    # the extension of the filename
    # @param value The value to set
    # -------------------------------------------------------------------------
    @filename_ext.setter
    def filename_ext(self, value):
        if value is None:
            logging.error('Can not set an empty extension')
            raise Exception('Can not set an empty extension')

        self.__full_filename = os.path.splitext(self.__full_filename)[0] + \
            value

    # -------------------------------------------------------------------------
    # test if the file exist
    # @return the result of the test
    # -------------------------------------------------------------------------
    def is_file(self):
        return (self.__full_filename is not None) and \
            (os.path.isfile(self.__full_filename))

    # -------------------------------------------------------------------------
    # test if the filename is en directory
    # @return the result of the test
    # -------------------------------------------------------------------------
    def is_dir(self):
        return (self.__full_filename is not None) and \
            (os.path.isdir(self.__full_filename))

    # -------------------------------------------------------------------------
    # __repr__ is a built-in function used to compute the "official"
    # string reputation of an object
    # __repr__ goal is to be unambiguous
    # -------------------------------------------------------------------------
    def __repr__(self):
        return self.__full_filename

    # -------------------------------------------------------------------------
    # __str__ is a built-in function that computes the "informal"
    # string reputation of an object
    # __str__ goal is to be readable
    # -------------------------------------------------------------------------
    def __str__(self):
        result = ""
        result += "          path=%s\n" % self.filename_path
        result += "      filename=%s\n" % self.filename
        result += "file extension=%s\n" % self.filename_ext

        if self.is_dir():
            result += "It is a directory\n"
            return result

        if self.is_file():
            result += "The file exists\n"
            return result

        result += "The file or the directory does not exist\n"
        return result

# -----------------------------------------------------------------------------
# Object for file content.
# Provide manipulation on file to get the content and handle the backup.
# Can be a object base for other purpose.
# -----------------------------------------------------------------------------
class FileContent(FileName):

    # -------------------------------------------------------------------------
    # Initialize the object from a content a filename or other
    #
    # @param filename the filename of the file
    # @param content the content of the file if needed
    # @param backup the backup option (if true each save generate a backup)
    # @param encoding the encoding to read the file
    # -------------------------------------------------------------------------
    def __init__(self, filename=None,
                 content=None,
                 backup=True,
                 encoding="utf-8"):

        # init the base class
        FileName.__init__(self, filename=filename)

        # set the first value
        self.__content = None
        self.__backup = None
        self.__save_needed = False

        # fill the data
        self.backup = backup

        # read the file if needed
        if (self.is_file()) and (content is None):
            self.__content = common.get_file_content(self.full_filename,
                                                     encoding=encoding)

        # set the content if needed
        if content is not None:
            self.content = content

    # -------------------------------------------------------------------------
    # Acces to the content
    # @return the value
    # -------------------------------------------------------------------------
    @property
    def content(self):
        return self.__content

    # -------------------------------------------------------------------------
    # Acces to the content
    # @param value The value to set
    # -------------------------------------------------------------------------
    @content.setter
    def content(self, value):
        self.__save_needed = True
        self.__content = value

    # -------------------------------------------------------------------------
    # Acces to the backup status
    # @return the value
    # -------------------------------------------------------------------------
    @property
    def backup(self):
        return self.__backup

    # -------------------------------------------------------------------------
    # Acces to the backup status
    # @param value The value to set
    # -------------------------------------------------------------------------
    @backup.setter
    def backup(self, value):
        self.__backup = bool(value)

    # -------------------------------------------------------------------------
    # Acces to the save needed status
    # @return the value
    # -------------------------------------------------------------------------
    @property
    def save_needed(self):
        return self.__save_needed

    # -------------------------------------------------------------------------
    # Acces to the save needed status
    # @param value The value to set
    # -------------------------------------------------------------------------
    @save_needed.setter
    def save_needed(self, value):
        self.__save_needed = bool(value)

    # -------------------------------------------------------------------------
    # Read the content of the filename
    # @param filename The filename if needed (this opion set the filename)
    # @param encoding The encoding of the file
    # -------------------------------------------------------------------------
    def read(self, filename=None, encoding="utf-8"):
        if filename is not None:
            self.full_filename = filename

        if self.full_filename is None:
            logging.error('Can not read the content without filename')
            raise Exception('Can not read the content without filename')

        self.content = common.get_file_content(self.full_filename,
                                               encoding=encoding)
        self.__save_needed = False

    # -------------------------------------------------------------------------
    # Write the content of the filename
    # @param filename The filename if needed (this opion set the filename)
    # @param encoding The encoding of the file
    # @param backup_ext The backup extension if needed
    # -------------------------------------------------------------------------
    def write(self, filename=None, backup_ext=".bak", encoding="utf-8"):
        if self.content is None:
            logging.error('Ther is no content to save to %s', self.filename)
            return

        if filename is not None:
            self.full_filename = filename

        if os.path.isfile(self.full_filename) and self.backup:
            common.create_backup(self.full_filename, backup_ext=backup_ext)

        if self.full_filename is None:
            logging.error('Can not save the content without filename')
            raise Exception('Can not save the content without filename')

        common.set_file_content(self.full_filename, self.content,
                                encoding=encoding)
        self.__save_needed = False

    # -------------------------------------------------------------------------
    # __repr__ is a built-in function used to compute the "official"
    # string reputation of an object
    # __repr__ goal is to be unambiguous
    # -------------------------------------------------------------------------
    def __repr__(self):
        return FileName.__repr__(self) + ":" + repr(self.__content)

    # -------------------------------------------------------------------------
    # __str__ is a built-in function that computes the "informal"
    # string reputation of an object
    # __str__ goal is to be readable
    # -------------------------------------------------------------------------
    def __str__(self):
        result = FileName.__str__(self)
        result += "backup option=%s\n" % self.backup
        result += "save needed=%s\n" % self.__save_needed

        if self.content is None:
            result += "Content is None"
            return result

        result += "Content char number=%6d\n" % len(self.content)

        return result


# -----------------------------------------------------------------------------
# Find the filename of this file (depend on the frozen or not)
# This function return the filename of this script.
# The function is complex for the frozen system
#
# @return the filename of THIS script.
# -----------------------------------------------------------------------------
def __get_this_filename():
    result = ""

    if getattr(sys, 'frozen', False):
        # frozen
        result = sys.executable
    else:
        # unfrozen
        result = __file__

    return result