|
| 1 | +""" |
| 2 | +Functions for working with filesystem paths. |
| 3 | +
|
| 4 | +The :func:`expandpath` function expands the tilde to $HOME and environment |
| 5 | +variables to their values. |
| 6 | +
|
| 7 | +The :func:`augpath` function creates variants of an existing path without |
| 8 | +having to spend multiple lines of code splitting it up and stitching it back |
| 9 | +together. |
| 10 | +
|
| 11 | +The :func:`shrinkuser` function replaces your home directory with a tilde. |
| 12 | +""" |
| 13 | +from __future__ import print_function |
| 14 | + |
| 15 | +from os.path import (expanduser, expandvars, join, normpath, split, splitext) |
| 16 | +import os |
| 17 | + |
| 18 | + |
| 19 | +__all__ = [ |
| 20 | + 'augpath', 'shrinkuser', 'expandpath', |
| 21 | +] |
| 22 | + |
| 23 | + |
| 24 | +def augpath(path, suffix='', prefix='', ext=None, base=None, dpath=None, |
| 25 | + multidot=False): |
| 26 | + """ |
| 27 | + Augment a path by modifying its components. |
| 28 | +
|
| 29 | + Creates a new path with a different extension, basename, directory, prefix, |
| 30 | + and/or suffix. |
| 31 | +
|
| 32 | + A prefix is inserted before the basename. A suffix is inserted |
| 33 | + between the basename and the extension. The basename and extension can be |
| 34 | + replaced with a new one. Essentially a path is broken down into components |
| 35 | + (dpath, base, ext), and then recombined as (dpath, prefix, base, suffix, |
| 36 | + ext) after replacing any specified component. |
| 37 | +
|
| 38 | + Args: |
| 39 | + path (str | PathLike): a path to augment |
| 40 | + suffix (str, default=''): placed between the basename and extension |
| 41 | + prefix (str, default=''): placed in front of the basename |
| 42 | + ext (str, default=None): if specified, replaces the extension |
| 43 | + base (str, default=None): if specified, replaces the basename without |
| 44 | + extension |
| 45 | + dpath (str | PathLike, default=None): if specified, replaces the |
| 46 | + directory |
| 47 | + multidot (bool, default=False): Allows extensions to contain multiple |
| 48 | + dots. Specifically, if False, everything after the last dot in the |
| 49 | + basename is the extension. If True, everything after the first dot |
| 50 | + in the basename is the extension. |
| 51 | +
|
| 52 | + Returns: |
| 53 | + str: augmented path |
| 54 | +
|
| 55 | + Example: |
| 56 | + >>> path = 'foo.bar' |
| 57 | + >>> suffix = '_suff' |
| 58 | + >>> prefix = 'pref_' |
| 59 | + >>> ext = '.baz' |
| 60 | + >>> newpath = augpath(path, suffix, prefix, ext=ext, base='bar') |
| 61 | + >>> print('newpath = %s' % (newpath,)) |
| 62 | + newpath = pref_bar_suff.baz |
| 63 | +
|
| 64 | + Example: |
| 65 | + >>> augpath('foo.bar') |
| 66 | + 'foo.bar' |
| 67 | + >>> augpath('foo.bar', ext='.BAZ') |
| 68 | + 'foo.BAZ' |
| 69 | + >>> augpath('foo.bar', suffix='_') |
| 70 | + 'foo_.bar' |
| 71 | + >>> augpath('foo.bar', prefix='_') |
| 72 | + '_foo.bar' |
| 73 | + >>> augpath('foo.bar', base='baz') |
| 74 | + 'baz.bar' |
| 75 | + >>> augpath('foo.tar.gz', ext='.zip', multidot=True) |
| 76 | + 'foo.zip' |
| 77 | + >>> augpath('foo.tar.gz', ext='.zip', multidot=False) |
| 78 | + 'foo.tar.zip' |
| 79 | + >>> augpath('foo.tar.gz', suffix='_new', multidot=True) |
| 80 | + 'foo_new.tar.gz' |
| 81 | + """ |
| 82 | + # Breakup path |
| 83 | + orig_dpath, fname = split(path) |
| 84 | + if multidot: |
| 85 | + # The first dot defines the extension |
| 86 | + parts = fname.split('.', 1) |
| 87 | + orig_base = parts[0] |
| 88 | + orig_ext = '' if len(parts) == 1 else '.' + parts[1] |
| 89 | + else: |
| 90 | + # The last dot defines the extension |
| 91 | + orig_base, orig_ext = splitext(fname) |
| 92 | + # Replace parts with specified augmentations |
| 93 | + if dpath is None: |
| 94 | + dpath = orig_dpath |
| 95 | + if ext is None: |
| 96 | + ext = orig_ext |
| 97 | + if base is None: |
| 98 | + base = orig_base |
| 99 | + # Recombine into new path |
| 100 | + new_fname = ''.join((prefix, base, suffix, ext)) |
| 101 | + newpath = join(dpath, new_fname) |
| 102 | + return newpath |
| 103 | + |
| 104 | + |
| 105 | +def shrinkuser(path, home='~'): |
| 106 | + """ |
| 107 | + Inverse of :func:`os.path.expanduser`. |
| 108 | +
|
| 109 | + Args: |
| 110 | + path (str | PathLike): path in system file structure |
| 111 | + home (str, default='~'): symbol used to replace the home path. |
| 112 | + Defaults to '~', but you might want to use '$HOME' or |
| 113 | + '%USERPROFILE%' instead. |
| 114 | +
|
| 115 | + Returns: |
| 116 | + str: path: shortened path replacing the home directory with a tilde |
| 117 | +
|
| 118 | + Example: |
| 119 | + >>> path = expanduser('~') |
| 120 | + >>> assert path != '~' |
| 121 | + >>> assert shrinkuser(path) == '~' |
| 122 | + >>> assert shrinkuser(path + '1') == path + '1' |
| 123 | + >>> assert shrinkuser(path + '/1') == join('~', '1') |
| 124 | + >>> assert shrinkuser(path + '/1', '$HOME') == join('$HOME', '1') |
| 125 | + """ |
| 126 | + path = normpath(path) |
| 127 | + userhome_dpath = expanduser('~') |
| 128 | + if path.startswith(userhome_dpath): |
| 129 | + if len(path) == len(userhome_dpath): |
| 130 | + path = home |
| 131 | + elif path[len(userhome_dpath)] == os.path.sep: |
| 132 | + path = home + path[len(userhome_dpath):] |
| 133 | + return path |
| 134 | + |
| 135 | + |
| 136 | +def expandpath(path): |
| 137 | + """ |
| 138 | + Shell-like expansion of environment variables and tilde home directory. |
| 139 | +
|
| 140 | + Args: |
| 141 | + path (str | PathLike): the path to expand |
| 142 | +
|
| 143 | + Returns: |
| 144 | + str : expanded path |
| 145 | +
|
| 146 | + Example: |
| 147 | + >>> import os |
| 148 | + >>> os.environ['SPAM'] = 'eggs' |
| 149 | + >>> assert expandpath('~/$SPAM') == expanduser('~/eggs') |
| 150 | + >>> assert expandpath('foo') == 'foo' |
| 151 | + """ |
| 152 | + path = expanduser(path) |
| 153 | + path = expandvars(path) |
| 154 | + return path |
0 commit comments