Ripped from a more elaborate bootstrap script. A sequential set of parent-directories were required for environment variables and subsequent, portable. auto-discovery.
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.