Welcome, guest | Sign In | My Account | Store | Cart

This recipe is a kind of extension to os.path It offers an entire split function and a true commonprefix for paths.

Python, 72 lines
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
"""Path utility to:

* split a path entirely
* join it
* return true commonprefix on paths

    >>> import os
    >>> path = os.getcwd()
    >>> join(split(path)) == path
    True

    >>> p1, p2, p3 = (os.path.join(*'abc'), os.path.join(*'abd'),
    ...               os.path.join(*'ab'))
    >>> commonprefix(p1, p2, p3) == p3
    True
    >>> p1, p2 = os.path.join(*'xyz'), os.path.join(*'abd')
    >>> commonprefix(p1, p2)
    ''
"""
import os.path

def isplit(path):
    "Generator splitting a path"
    dirname, basename = os.path.split(path)
    if path == dirname:
        # stop recursivity
        yield path
    elif dirname:
        # continue recursivity
        for i in isplit(dirname):
            yield i
    if basename:
        # return tail
        yield basename

def join(iterable):
    """Join iterable's items as a path string

    >>> join(('a', 'b')) == os.path.join('a', 'b')
    True
    """
    items = tuple(iterable)
    if not items:
        return ''
    return os.path.join(*items)
    
def split(path):
    """Return the folder list of the given path

    >>> split(os.path.join('a', 'b'))
    ('a', 'b')
    """
    return tuple(isplit(path))

def commonprefix(*paths):
    """Return the common prefix path of the given paths

    >>> commonprefix(os.path.join('a', 'c'), os.path.join('a', 'b'))
    'a'
    """
    paths = map(split, paths)
    if not paths: return ''
    p1 = min(paths)
    p2 = max(paths)
    for i, c in enumerate(p1):
        if c != p2[i]:
            return join(p1[:i])
    return join(p1)

if __name__ == '__main__':
    import doctest
    doctest.testmod()

2 comments

Craig McQueen 13 years, 8 months ago  # | flag

Great idea!

Unfortunately, it doesn't work so well if the paths have no common prefix:

>>> commonprefix('a/b', 'c/d')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "recipe_577016_1.py", line 70, in commonprefix
    return join(map(_first, takewhile(_equal_elem, tuples)))
  File "recipe_577016_1.py", line 48, in join
    return os.path.join(*iterable)
TypeError: join() takes at least 1 argument (0 given)
Maxime Fontenier (author) 12 years, 12 months ago  # | flag

Thanks for the notice:it is now fixed.

It read the os.path version and us it to shorten this recipe.