Source code for ostrich.utils.path

# -*- coding: utf-8 -*-
# pylint: disable=redefined-builtin, redefined-variable-type


"""
path utils module
"""


# Python 2 / Python 3 compatibility fu
# http://python-future.org/compatible_idioms.html
from __future__ import absolute_import
from __future__ import unicode_literals  # so strings without u'' are unicode

# uniform unicode type across Python's
from builtins import bytes, str  # from "future" library

import os


def check_arg_types(funcname, *args):
    """Raise TypeError if not all items of `args` are same string type."""
    hasstr = hasbytes = False
    for arg in args:
        if isinstance(arg, str):
            hasstr = True
        elif isinstance(arg, bytes):
            hasbytes = True
        else:
            raise TypeError('{0}() argument must be str or bytes, not {1}'
                            .format(funcname, arg.__class__.__name__))
    if hasstr and hasbytes:
        raise TypeError("Can't mix strings and bytes in path components")


def posix_commonpath(paths):
    """Given a sequence of POSIX path names,
       return the longest common sub-path."""

    if not paths:
        raise ValueError('commonpath() arg is an empty sequence')

    check_arg_types('commonpath', *paths)

    if isinstance(paths[0], bytes):
        sep = b'/'
        curdir = b'.'
    else:
        sep = '/'
        curdir = '.'

    split_paths = [path.split(sep) for path in paths]

    try:
        isabs, = set(p[:1] == sep for p in paths)
    except ValueError:
        raise ValueError("Can't mix absolute and relative paths")

    split_paths = [[c for c in s if c and c != curdir] for s in split_paths]
    s_min = min(split_paths)
    s_max = max(split_paths)
    common = s_min
    for i, run_c in enumerate(s_min):
        if run_c != s_max[i]:
            common = s_min[:i]
            break

    prefix = sep if isabs else sep[:0]
    return prefix + sep.join(common)


def nt_commonpath(paths):  # pylint: disable=too-many-locals
    """Given a sequence of NT path names,
       return the longest common sub-path."""

    from ntpath import splitdrive

    if not paths:
        raise ValueError('commonpath() arg is an empty sequence')

    check_arg_types('commonpath', *paths)

    if isinstance(paths[0], bytes):
        sep = b'\\'
        altsep = b'/'
        curdir = b'.'
    else:
        sep = '\\'
        altsep = '/'
        curdir = '.'

    drivesplits = [splitdrive(p.replace(altsep, sep).lower()) for p in paths]
    split_paths = [p.split(sep) for d, p in drivesplits]

    try:
        isabs, = set(p[:1] == sep for d, p in drivesplits)
    except ValueError:
        raise ValueError("Can't mix absolute and relative paths")

    # Check that all drive letters or UNC paths match. The check is made
    # only now otherwise type errors for mixing strings and bytes would not
    # be caught.
    if len(set(d for d, p in drivesplits)) != 1:
        raise ValueError("Paths don't have the same drive")

    drive, path = splitdrive(paths[0].replace(altsep, sep))
    common = path.split(sep)
    common = [c for c in common if c and c != curdir]

    split_paths = [[c for c in s if c and c != curdir] for s in split_paths]
    s_min = min(split_paths)
    s_max = max(split_paths)
    for i, run_c in enumerate(s_min):
        if run_c != s_max[i]:
            common = common[:i]
            break
    else:
        common = common[:len(s_min)]

    prefix = drive + sep if isabs else drive
    return prefix + sep.join(common)


[docs]def commonpath(paths): """Return the longest common sub-path of each pathname in paths sequence. Raise ValueError if paths contains both absolute and relative pathnames, or if paths is empty. Unlike :py:func:`os.path.commonprefix()`, this returns a valid path: >>> print(commonpath(['foo/bar', 'foo/baz', 'foo/baaam'])) foo >>> from os.path import commonprefix >>> print(commonprefix(['foo/bar', 'foo/baz', 'foo/baaam'])) foo/ba Adapted from the source code of Python 3.5.1. """ if os.name == 'posix': return posix_commonpath(paths) return nt_commonpath(paths)