1 # Copyright (c) 2008 David Aguilar
2 """This module provides miscellaneous utility functions."""
3 from __future__
import division
, absolute_import
, unicode_literals
15 random
.seed(hash(time
.time()))
18 def add_parents(paths
):
19 """Iterate over each item in the set and add its parent directories."""
23 path
= path
.replace('//', '/')
26 parent_dir
= dirname(path
)
28 all_paths
.add(parent_dir
)
29 parent_dir
= dirname(parent_dir
)
33 def format_exception(e
):
34 exc_type
, exc_value
, exc_tb
= sys
.exc_info()
35 details
= traceback
.format_exception(exc_type
, exc_value
, exc_tb
)
36 details
= '\n'.join(map(core
.decode
, details
))
40 msg
= core
.decode(repr(e
))
45 """Subtracts list b from list a and returns the resulting list."""
46 # conceptually, c = a - b
57 def grep(pattern
, items
, squash
=True):
58 """Greps a list for items that match a pattern
60 :param squash: If only one item matches, return just that item
61 :returns: List of matching items
64 isdict
= type(items
) is dict
65 if pattern
in __grep_cache
:
66 regex
= __grep_cache
[pattern
]
68 regex
= __grep_cache
[pattern
] = re
.compile(pattern
)
72 match
= regex
.match(item
)
75 groups
= match
.groups()
77 subitems
= match
.group(0)
82 subitems
= list(groups
)
84 matchdict
[item
] = items
[item
]
86 matched
.append(subitems
)
91 if squash
and len(matched
) == 1:
99 An os.path.basename() implementation that always uses '/'
101 Avoid os.path.basename because git's output always
102 uses '/' regardless of platform.
105 return path
.rsplit('/', 1)[-1]
109 """Strip one level of directory"""
110 return path
.strip('/').split('/', 1)[-1]
113 def dirname(path
, current_dir
=''):
115 An os.path.dirname() implementation that always uses '/'
117 Avoid os.path.dirname because git's output always
118 uses '/' regardless of platform.
122 path
= path
.replace('//', '/')
123 path_dirname
= path
.rsplit('/', 1)[0]
124 if path_dirname
== path
:
126 return path
.rsplit('/', 1)[0]
129 def select_directory(paths
):
130 """Return the first directory in a list of paths"""
138 return os
.path
.dirname(paths
[0])
141 def strip_prefix(prefix
, string
):
142 """Return string, without the prefix. Blow up if string doesn't
143 start with prefix."""
144 assert string
.startswith(prefix
)
145 return string
[len(prefix
):]
149 """Removes shell metacharacters from a string."""
150 for c
in """ \t!@#$%^&*()\\;,<>"'[]{}~|""":
151 s
= s
.replace(c
, '_')
155 def tablength(word
, tabwidth
):
156 """Return length of a word taking tabs into account
158 >>> tablength("\\t\\t\\t\\tX", 8)
162 return len(word
.replace('\t', '')) + word
.count('\t') * tabwidth
166 """Split string apart into utf-8 encoded words using shell syntax"""
168 return shlex
.split(core
.encode(s
))
170 return [core
.encode(s
)]
173 if sys
.version_info
[0] == 3:
174 # In Python 3, we don't need the encode/decode dance
175 shell_split
= shlex
.split
178 """Returns a unicode list instead of encoded strings"""
179 return [core
.decode(arg
) for arg
in _shell_split(s
)]
182 def tmp_filename(label
):
183 label
= 'git-cola-' + label
.replace('/', '-').replace('\\', '-')
184 fd
= tempfile
.NamedTemporaryFile(prefix
=label
+'-')
190 """Is this a linux machine?"""
191 return sys
.platform
.startswith('linux')
196 return os
.path
.exists('/usr/bin/apt-get')
200 """Return True on OSX."""
201 return sys
.platform
== 'darwin'
205 """Return True on win32"""
206 return sys
.platform
== 'win32' or sys
.platform
== 'cygwin'
209 def expandpath(path
):
210 """Expand ~user/ and environment $variables"""
211 path
= os
.path
.expandvars(path
)
212 if path
.startswith('~'):
213 path
= os
.path
.expanduser(path
)
218 """Operate on a collection of objects as a single unit"""
220 def __init__(self
, *members
):
221 self
._members
= members
223 def __getattr__(self
, name
):
224 """Return a function that relays calls to the group"""
225 def relay(*args
, **kwargs
):
226 for member
in self
._members
:
227 method
= getattr(member
, name
)
228 method(*args
, **kwargs
)
229 setattr(self
, name
, relay
)
234 """Wrap an object and override attributes"""
236 def __init__(self
, obj
, **overrides
):
238 for k
, v
in overrides
.items():
241 def __getattr__(self
, name
):
242 return getattr(self
._obj
, name
)