maestral.utils.path =================== .. py:module:: maestral.utils.path .. autoapi-nested-parse:: This module contains functions for common path operations. Module Contents --------------- .. py:data:: F_GETPATH :value: 50 .. py:function:: is_child(path, parent, case_sensitive = True) Checks if ``path`` semantically is inside ``parent``. Neither path needs to refer to an actual item on the drive. This function is case-sensitive. :param path: Item path. :param parent: Parent path. :param case_sensitive: Whether to do case-sensitive checks. :returns: Whether ``path`` semantically lies inside ``parent``. .. py:function:: is_equal_or_child(path, parent, case_sensitive = True) Checks if ``path`` semantically is inside ``parent`` or equals ``parent``. Neither path needs to refer to an actual item on the drive. This function is case-sensitive. :param path: Item path. :param parent: Parent path. :param case_sensitive: Whether to do case-sensitive checks. :returns: ``True`` if ``path`` semantically lies inside ``parent`` or ``path == parent``. .. py:function:: normalize_case(string) Converts a string to lower case. Todo: Follow Python 2.5 / Dropbox conventions. :param string: Original string. :returns: Lowercase string. .. py:function:: normalize_unicode(string) Normalizes a string to replace all decomposed unicode characters with their single character equivalents. :param string: Original string. :returns: Normalized string. .. py:function:: normalize(string) Replicates the path normalization performed by Dropbox servers. This typically only involves converting the path to lower case, with a few (undocumented) exceptions: * Unicode normalization: decomposed characters are converted to composed characters. * Lower casing of non-ascii characters: Dropbox uses the Python 2.5 behavior for conversion to lower case. This means that some cyrillic characters are incorrectly lower-cased. For example: "Ꙋ".lower() -> "Ꙋ" instead of "ꙋ" "ΣΣΣ".lower() -> "σσσ" instead of "σσς" * Trailing spaces are stripped from folder names. We do not perform this normalization here because the Dropbox API will raise sync errors for such names anyways. Note that calling :func:`normalize` on an already normalized path will return the unmodified input. Todo: Follow Python 2.5 / Dropbox conventions instead of Python 3 conventions. :param string: Original path. :returns: Normalized path. .. py:function:: is_fs_case_sensitive(path) Checks if ``path`` lies on a partition with a case-sensitive file system. :param path: Path to check. :returns: Whether ``path`` lies on a partition with a case-sensitive file system. .. py:function:: get_existing_equivalent_paths(path, root = osp.sep, norm_func = normalize) Given a "normalized" path using an injective (one-directional) normalization function, this method returns a list of matching un-normalized local paths. If no such local paths exist, list will be empty. :Example: Assume the normalization function is ``str.lower()``. If a root directory contains two folders "/parent/subfolder/child" and "/parent/Subfolder/child", two matches will be returned for "path = /parent/subfolder/child/file.txt". :param path: Normalized path relative to ``root``. :param root: Parent directory to search in. There are significant performance improvements if a root directory with a small tree is given. :param norm_func: Normalization function to use. Defaults to :func:`normalize`. :returns: List of existing paths for which `normalized(local_path) == normalized(path)`. .. py:function:: to_existing_unnormalized_path(path, root = osp.sep, norm_func = normalize) Returns a cased version of the given path if corresponding nodes (with arbitrary casing) exist in the given root directory. If multiple matches are found, only one is returned. This is similar to :func:`get_existing_equivalent_paths` but returns only the first candidate or raises a :class:`FileNotFoundError` if no candidates can be found. If the file system is not case-sensitive but case-preserving, this function effectively returns the "displayed" version of a path, as used for example in file managers. On macOS, we use fcntl F_GETPATH for a more efficient implementation. :param path: Original path relative to ``root``. :param root: Parent directory to search in. There are significant performance improvements if a root directory with a small tree is given. :param norm_func: Normalization function to use. Defaults to :func:`normalize`. :returns: Absolute and cased version of given path. :raises FileNotFoundError: if ``path`` does not exist in root ``root`` or ``root`` itself does not exist. .. py:function:: normalized_path_exists(path, root = osp.sep) Checks if a ``path`` exists in given ``root`` directory, similar to ``os.path.exists`` but case-insensitive. Normalisation is performed as by Dropbox servers (lower case and unicode normalisation). :param path: Path relative to ``root``. :param root: Directory where we will look for ``path``. There are significant performance improvements if a root directory with a small tree is given. :returns: Whether an arbitrarily cased version of ``path`` exists. .. py:function:: generate_cc_name(path, suffix) Generates a path for a conflicting copy of ``path``. The file name is created by inserting the given ``suffix`` between the filename and the extension. For example, for ``suffix = "conflicting copy"``: "my_file.txt" -> "my_file (conflicting copy).txt" If a file with the resulting path already exists (case-insensitive!), we additionally append an integer number, for instance: "my_file.txt" -> "my_file (conflicting copy 1).txt" :param path: Original path name. :param suffix: Suffix to use. :returns: New path. .. py:function:: delete(path, force_case_sensitive = False, raise_error = False) Deletes a file or folder at ``path``. Symlinks will not be followed. :param path: Path of item to delete. :param force_case_sensitive: Whether to perform the deletion only if the item appears with the same casing as provided in `path`. This can be used on case-insensitive but preserving file systems to ensure that the intended item is deleted. :param raise_error: Whether to raise errors or return them. :returns: Any caught exception during the deletion. .. py:function:: move(src_path, dest_path, raise_error = False, keep_target_permissions = False, keep_target_xattrs = False) Moves a file or folder from ``src_path`` to ``dest_path``. If either the source or the destination path no longer exist, this function does nothing. Any other exceptions are either raised or returned if ``raise_error`` is False. Uses ``os.rename`` internally. :param src_path: Path of item to move. :param dest_path: Destination path. Any existing file at this path will be replaced by the move. Any existing **empty** folder will be replaced if the source is also a folder. :param raise_error: Whether to raise errors or return them. :param keep_target_permissions: Whether to preserve the permissions of a file at the destination, if any. :param keep_target_xattrs: Whether to preserve the extended attributes of a file at the destination, if any. :returns: Any caught exception during the move. .. py:function:: walk(root, listdir = os.scandir) Iterates recursively over the content of a folder. :param root: Root folder to walk. :param listdir: Function to call to get the folder content. :returns: Iterator over (path, stat) results. .. py:function:: content_hash(local_path, chunk_size = 65536) Computes content hash of a local file. :param local_path: Absolute path on local drive. :param chunk_size: Size of chunks to hash in bytes. :returns: Content hash to compare with Dropbox's content hash and mtime just before the hash was computed. .. py:function:: fs_max_lengths_for_path(path = '/') Return the maximum length of file names and paths allowed on a file system. :param path: Path to check. This can be specified because different paths may be residing on different file systems. If the given path does not exist, the first existing parent directory in the tree be taken. :returns: Tuple giving the maximum file name and total path lengths. .. py:function:: opener_no_symlink(path, flags) Opener that does not follow symlinks. Uses :meth:`os.open` under the hood. :param path: Path to open. :param flags: Flags passed to :meth:`os.open`. O_NOFOLLOW will be added. :return: Open file descriptor. .. py:function:: exists(path) Returns whether an item exists at the path. Returns True for symlinks. .. py:function:: isfile(path) Returns whether a file exists at the path. Returns True for symlinks. .. py:function:: isdir(path) Returns whether a folder exists at the path. Returns False for symlinks. .. py:function:: getsize(path) Returns the size. Returns False for symlinks. .. py:function:: equal_but_for_unicode_norm(s0, s1) .. py:function:: get_symlink_target(local_path) Returns the symlink target of a file. :param local_path: Absolute path on local drive. :returns: Symlink target of local file. None if the local path does not refer to a symlink or does not exist.