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

Ripped from a more elaborate bootstrap script. A sequential set of parent-directories were required for environment variables and subsequent, portable. auto-discovery.

Python, 44 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
#!/usr/bin/env python

"""
Create a dictionary of ascending (sequential) directories.
"""

def make_pathdict(keys, fspath = None):

    """Quickly create a dictionary of ascending path components.
    
    :param keys: list of dictionary keys (base -> root order)
    :returns: dictionary of keyed paths

    NOTICE: This does not check path-length::key-count, etc.!
            Also, not as robust as os.path in x-platform use.

    >>> fspath = "/and/the/player/asks/anyone/for_tennis.py"
    >>> keys = "base midl root".split()
    >>> ret_dict = make_pathdict(keys, fspath)
    >>> for k in keys: print "{0:<6}{1}".format(k, ret_dict[k])
    base  /and/the/player/asks/anyone
    midl  /and/the/player/asks
    root  /and/the/player
    """
    
    from os import path as os_path

    _cache = {}

    fspath = os_path.abspath(fspath or __file__)


    # divide the path into len(keys) + 1 parts, the root, directories and file

    tokenz = fspath.rsplit(os_path.sep, len(keys))


    # iterate the keys assigning the decreasing-lenght path-portions

    for idx, key in enumerate(keys):
        _cache[key] = os_path.join(*tokenz[:-(idx + 1)])


    return _cache

Per the intro, this has been ripped from a bootstrap script I (just) penned. Its usefulness appears ubiquitous enough to warrant offering it here.

A similar technique was used to add extra directories at the root-level, once discovered. A list of key:sub-path pairs and the project root-path. Indeed it would be easy to modify the above to accommodate both.

It is tiresome to code the above as:

start_path = "/some/long/path/of/extraordinary/uselessness.txt"
base_parent, base_name = os.path.split(start_path)
parents_parent, parent_name = os.path.split(base_parent)
parents_parents_parent, parents_name = os.path.split(parents_parent)

 path_dict['base_parent'] = base_parent
path_dict['parents_parent'] = parents_parent
path_dict['parents_parents_parent'] = parents_parents_parent

YAWN!

The 'rsplit' tidily retains the root-portion integrity, thus eliminating extra slicing and path-joining. This string function tokenizes the remaining path-portions of directories and file. In this case it is flexibly based upon the length of supplied keys.

The original script uses key directory-path names to help reduce erroneous initialization while enabling the script or module to be moved with less risk of breaking* things:

assert tokenz[-2:-1] == ['asks', 'anyone']

... yes, there are other ways i.e.

assert path_bit.endswith('asks/anyone')

... then again someone might need to "wild-card" the middle directory:

assert (tokens[-3], tokens[-1) == ('player', 'anyone')

For alternates, I considered using a shorter version of these constructs:

path_list = []
use_pop = True

while len(tokenz) > 1:
    path_list.append(os.path.join(*tokenz))

    if use_pop:
        tokens.pop()

    elif not use_pop: # a.k.a. slice
        tokenz = tokez[:-1]

    else:
        print "weeeeeee....."


return dict(zip(keys, path_list))

The initial motivation* was to aid in auto-discovering required config files when a module was loaded. In some circumstances, like manually testing (*it happens). This can be hidden when called/loaded as a module, as I have, within this:

if __name__ == "__main__":
    key_list = [...]
    path_dict = make_pathdict(keys)

Due to long sessions un-tying some incredibly-nasty, hard-coded, uber-redundant, spaghetti-source [originally] spread across a host server... *nice! Just call this post therapy and its all good.

Edit due to dumb formatting error.