SPPAS 4.20

Module sppas.src.wkps

Class FilePath

Description

Represent the data linked to a folder name.

Use instances of this class to hold data related to the path of

a filename. Items in the tree will get associated back to the

corresponding FileName and this FilePath object.

Constructor

Constructor of a FilePath.

Parameters
  • filepath: (str) Absolute or relative name of a folder
Raises

PathTypeError

View Source
def __init__(self, filepath):
    """Constructor of a FilePath.

    :param filepath: (str) Absolute or relative name of a folder
    :raises: PathTypeError

    """
    if os.path.exists(filepath) is False:
        rel_path = os.path.abspath(filepath)
        if os.path.exists(rel_path) is True:
            filepath = rel_path
    super(FilePath, self).__init__(filepath)
    if os.path.exists(filepath) is False:
        self._state = States().MISSING
    elif os.path.isdir(filepath) is False:
        raise PathTypeError(filepath)
    self.__roots = list()
    self.subjoined = None

Public functions

set_object_state

Set a state value to a filename of this filepath.

It is not allowed to manually assign one of the "AT_LEAST" states.

They are automatically fixed here depending on the roots states.

Parameters
  • value: (int) A state.
  • entry: (FileName, FileRoot) The instance to change state
Raises

sppasTypeError, sppasOSError, sppasValueError

View Source
def set_object_state(self, value, entry):
    """Set a state value to a filename of this filepath.

        It is not allowed to manually assign one of the "AT_LEAST" states.
        They are automatically fixed here depending on the roots states.

        :param value: (int) A state.
        :param entry: (FileName, FileRoot) The instance to change state
        :raises: sppasTypeError, sppasOSError, sppasValueError

        """
    modified = list()
    if isinstance(entry, (FileName, FileRoot)) is False:
        entry = FileName(entry)
    if isinstance(entry, FileName):
        root_id = FileRoot.root(entry.id)
        fr = self.get_root(root_id)
        if fr is None:
            raise sppasValueError(root_id, self.id)
        modified = fr.set_object_state(value, entry)
    elif isinstance(entry, FileRoot):
        modified = entry.set_state(value)
    if len(modified) > 0:
        m = self.update_state()
        if m is True:
            modified.append(self)
    return modified
set_state

Set a value to represent the state of the path.

It is not allowed to manually assign one of the "AT_LEAST" states

(they are automatically fixed by setting the state of a FileName

with setobjectstate method).

The state of LOCKED files is not changed.

Parameters
  • value: (State) A state of FileName.
View Source
def set_state(self, value):
    """Set a value to represent the state of the path.

        It is not allowed to manually assign one of the "AT_LEAST" states
        (they are automatically fixed by setting the state of a FileName
        with set_object_state method).

        The state of LOCKED files is not changed.

        :param value: (State) A state of FileName.

        """
    if value not in FileName.FILENAME_STATES:
        raise sppasTypeError(value, str(FileName.FILENAME_STATES))
    modified = list()
    for fr in self.__roots:
        m = fr.set_state(value)
        modified.extend(m)
    if len(modified) > 0:
        m = self.update_state()
        if m is True:
            modified.append(self)
    return modified
get_object

Return the instance matching the given entry.

Parameters
  • filename: Name of a file or a root(absolute of relative)

Notice that it returns 'self' if filename is a directory matching

self.id.

View Source
def get_object(self, filename):
    """Return the instance matching the given entry.

        :param filename: Name of a file or a root (absolute of relative)

        Notice that it returns 'self' if filename is a directory matching
        self.id.

        """
    abs_name = os.path.abspath(filename)
    if abs_name == self.id:
        return self
    for fr in self.__roots:
        if fr.id == abs_name:
            return fr
        fn = fr.get_object(filename)
        if fn is not None:
            return fn
    return None
identifier

Return the identifier, i.e. the full name of an existing file.

Parameters
  • filename: (str) Absolute or relative name of a file
Returns
  • (str) Identifier for this filename
Raises

FileOSError if filename does not match a regular file

View Source
def identifier(self, filename):
    """Return the identifier, i.e. the full name of an existing file.

        :param filename: (str) Absolute or relative name of a file
        :returns: (str) Identifier for this filename
        :raise: FileOSError if filename does not match a regular file

        """
    f = os.path.abspath(filename)
    if os.path.isfile(f) is False:
        f = os.path.join(self.id, filename)
    if os.path.isfile(f) is False:
        raise FileOSError(filename)
    return f
get_root

Return the FileRoot matching the given id (root or file).

Parameters
  • name: (str) Identifier name of a root or a file.
Returns
  • FileRoot or None
View Source
def get_root(self, name):
    """Return the FileRoot matching the given id (root or file).

        :param name: (str) Identifier name of a root or a file.
        :returns: FileRoot or None

        """
    for fr in self.__roots:
        if fr.id == name:
            return fr
    for fr in self.__roots:
        for fn in fr:
            if fn.id == name:
                return fr
    return None
append

Append a filename in the list of files.

Given filename can be either an absolute or relative name of a file

or an instance of FileName. It can also be an instance of FileRoot.

Only an existing file can be added if the given entry is the name of the file.

But any FileName() instance can be added, even if the file does not exists

(of course, its path must match with this fp).

Parameters
  • entry: (str, FileName, FileRoot) Absolute or relative name of a file
  • all_root: (bool) Add also all files sharing the same root as the given one, or all files of the given root
  • ctime: (float) Add files only if created/modified after time in seconds since the epoch
Returns
  • (FileName, FileRoot) the list of appended objects or None
Raises

FileOSError if entry is a non-existing filename. Exception if given entry does not math the path.

View Source
def append(self, entry, all_root=False, ctime=0.0):
    """Append a filename in the list of files.

        Given filename can be either an absolute or relative name of a file
        or an instance of FileName. It can also be an instance of FileRoot.

        Only an existing file can be added if the given entry is the name of the file.
        But any FileName() instance can be added, even if the file does not exists
        (of course, its path must match with this fp).

        :param entry: (str, FileName, FileRoot) Absolute or relative name of a file
        :param all_root: (bool) Add also all files sharing the same root as the given one, or all files of the given root
        :param ctime: (float) Add files only if created/modified after time in seconds since the epoch
        :returns: (FileName, FileRoot) the list of appended objects or None
        :raises: FileOSError if entry is a non-existing filename. Exception if given entry does not math the path.

        """
    new_objs = list()
    new_files = list()
    if isinstance(entry, FileRoot):
        abs_name = os.path.dirname(entry.id)
        if abs_name != self.id:
            logging.debug("The root {:s} can't be appended: it is not matching the FilePath {:s}".format(entry.id, self.id))
            raise FilesMatchingValueError(entry.id, self.id)
        obj = self.get_root(entry.id)
        if obj is None:
            self.__roots.append(entry)
            new_objs.append(entry)
        if all_root is True:
            new_files = entry.append(None, all_root=all_root, ctime=ctime)
    else:
        if isinstance(entry, FileName):
            file_id = entry.id
        else:
            file_id = self.identifier(entry)
            entry = FileName(file_id)
        abs_name = os.path.dirname(file_id)
        if abs_name != self.id:
            logging.debug("The file {:s} can't be appended: its path is not matching the FilePath {:s}".format(file_id, self.id))
            raise FilesMatchingValueError(file_id, self.id)
        root_id = FileRoot.root(file_id)
        fr = self.get_root(root_id)
        if fr is None:
            fr = FileRoot(root_id)
            self.__roots.append(fr)
            new_objs.append(fr)
        new_files = fr.append(entry, all_root=all_root, ctime=ctime)
    if len(new_files) > 0:
        new_objs.extend(new_files)
    if len(new_objs) > 0:
        self.update_state()
        return new_objs
    return None
remove

Remove a root entry of the list of roots.

Given entry can be either the identifier of a root or an instance

of FileRoot.

TODO: REMOVE IF ENTRY is FILENAME

Parameters
  • entry
Returns
  • (identifier) Identifier of the removed entry or None
View Source
def remove(self, entry):
    """Remove a root entry of the list of roots.

        Given entry can be either the identifier of a root or an instance
        of FileRoot.

        TODO: REMOVE IF ENTRY is FILENAME

        :param entry:
        :returns: (identifier) Identifier of the removed entry or None

        """
    if isinstance(entry, FileRoot):
        root = entry
    else:
        root = self.get_root(entry)
    try:
        idx = self.__roots.index(root)
        identifier = self.__roots[idx].get_id()
        self.__roots.pop(idx)
    except ValueError:
        identifier = None
    self.update_state()
    return identifier
unlock

Unlock all.

Returns
  • number of unlocked filenames
View Source
def unlock(self):
    """Unlock all.

        :returns: number of unlocked filenames

        """
    i = 0
    for fr in self.__roots:
        for fn in fr:
            if fn.get_state() == States().LOCKED:
                fn.set_state(States().CHECKED)
                i += 1
        if i > 0:
            fr.update_state()
    if i > 0:
        self.update_state()
    return i
update_state

Modify state depending on the checked root names.

The state is missing if the path is not existing on disk.

Else, the state of a path is assigned as it:

  • locked if all roots are locked,
  • atleastone_locked if at least one of its roots is locked,
  • checked if all roots are checked,
  • atleastone_checked if at least one of its roots is checked and none of the others are locked,
  • unused if none of its roots are neither locked, checked nor missing.
Returns
  • (bool) State was changed or not.
View Source
def update_state(self):
    """Modify state depending on the checked root names.

        The state is missing if the path is not existing on disk.
        Else, the state of a path is assigned as it:
            - locked if all roots are locked,
            - at_least_one_locked if at least one of its roots is locked,
            - checked if all roots are checked,
            - at_least_one_checked if at least one of its roots is checked and none of the others are locked,
            - unused if none of its roots are neither locked, checked nor missing.

        :return: (bool) State was changed or not.

        """
    state = States()
    if os.path.exists(self.get_id()) is False:
        if self._state == state.MISSING:
            return False
        else:
            self._state = state.MISSING
            return True
    if len(self.__roots) == 0:
        new_state = state.UNUSED
    else:
        at_least_checked = 0
        at_least_locked = 0
        checked = 0
        locked = 0
        for fr in self.__roots:
            if fr.get_state() == state.CHECKED:
                checked += 1
            elif fr.get_state() == state.AT_LEAST_ONE_CHECKED:
                at_least_checked += 1
            elif fr.get_state() == state.LOCKED:
                locked += 1
            elif fr.get_state() == state.AT_LEAST_ONE_LOCKED:
                at_least_locked += 1
        if locked == len(self.__roots):
            new_state = state.LOCKED
        elif locked + at_least_locked > 0:
            new_state = state.AT_LEAST_ONE_LOCKED
        elif checked == len(self.__roots):
            new_state = state.CHECKED
        elif at_least_checked + checked > 0:
            new_state = state.AT_LEAST_ONE_CHECKED
        else:
            new_state = state.UNUSED
    if self._state != new_state:
        self._state = new_state
        return True
    return False

Overloads

__repr__
View Source
def __repr__(self):
    return 'Path: ' + self.get_id() + ' contains ' + str(len(self.__roots)) + ' file roots\n'
__iter__
View Source
def __iter__(self):
    for a in self.__roots:
        yield a
__getitem__
View Source
def __getitem__(self, i):
    return self.__roots[i]
__len__
View Source
def __len__(self):
    return len(self.__roots)
__contains__
View Source
def __contains__(self, value):
    if isinstance(value, FileRoot):
        return value in self.__roots
    for fr in self.__roots:
        x = value in fr
        if x is True:
            return True
    root_id = FileRoot.root(value)
    fr = self.get_root(root_id)
    if fr is not None:
        return True
    return False