1 # Copyright (c) 2008 David Aguilar
2 """This module provides miscellaneous utility functions."""
3 from __future__
import division
, absolute_import
, unicode_literals
16 random
.seed(hash(time
.time()))
19 def add_parents(paths
):
20 """Iterate over each item in the set and add its parent directories."""
21 path_entry_set
= set(paths
)
22 for path
in list(path_entry_set
):
24 path
= path
.replace('//', '/')
25 if path
not in path_entry_set
:
26 path_entry_set
.add(path
)
28 parent_dir
= dirname(path
)
29 while parent_dir
and parent_dir
not in path_entry_set
:
30 path_entry_set
.add(parent_dir
)
31 parent_dir
= dirname(parent_dir
)
35 def format_exception(e
):
36 exc_type
, exc_value
, exc_tb
= sys
.exc_info()
37 details
= traceback
.format_exception(exc_type
, exc_value
, exc_tb
)
38 details
= '\n'.join(details
)
47 """Subtracts list b from list a and returns the resulting list."""
48 # conceptually, c = a - b
57 def grep(pattern
, items
, squash
=True):
58 """Greps a list for items that match a pattern and return a list of
59 matching items. If only one item matches, return just that item.
61 isdict
= type(items
) is dict
62 if pattern
in __grep_cache
:
63 regex
= __grep_cache
[pattern
]
65 regex
= __grep_cache
[pattern
] = re
.compile(pattern
)
69 match
= regex
.match(item
)
72 groups
= match
.groups()
74 subitems
= match
.group(0)
79 subitems
= list(groups
)
81 matchdict
[item
] = items
[item
]
83 matched
.append(subitems
)
88 if squash
and len(matched
) == 1:
96 An os.path.basename() implementation that always uses '/'
98 Avoid os.path.basename because git's output always
99 uses '/' regardless of platform.
102 return path
.rsplit('/', 1)[-1]
106 """Strip one level of directory
108 >>> strip_one('/usr/bin/git')
111 >>> strip_one('local/bin/git')
114 >>> strip_one('bin/git')
121 return path
.strip('/').split('/', 1)[-1]
126 An os.path.dirname() implementation that always uses '/'
128 Avoid os.path.dirname because git's output always
129 uses '/' regardless of platform.
133 path
= path
.replace('//', '/')
134 path_dirname
= path
.rsplit('/', 1)[0]
135 if path_dirname
== path
:
137 return path
.rsplit('/', 1)[0]
140 def strip_prefix(prefix
, string
):
141 """Return string, without the prefix. Blow up if string doesn't
142 start with prefix."""
143 assert string
.startswith(prefix
)
144 return string
[len(prefix
):]
148 """Removes shell metacharacters from a string."""
149 for c
in """ \t!@#$%^&*()\\;,<>"'[]{}~|""":
150 s
= s
.replace(c
, '_')
154 def tablength(word
, tabwidth
):
155 """Return length of a word taking tabs into account
157 >>> tablength("\\t\\t\\t\\tX", 8)
161 return len(word
.replace('\t', '')) + word
.count('\t') * tabwidth
165 """Split string apart into utf-8 encoded words using shell syntax"""
167 return shlex
.split(core
.encode(s
))
169 return [core
.encode(s
)]
172 if sys
.version_info
[0] == 3:
173 # In Python 3, we don't need the encode/decode dance
174 shell_split
= shlex
.split
177 """Returns a unicode list instead of encoded strings"""
178 return [core
.decode(arg
) for arg
in _shell_split(s
)]
181 def tmp_file_pattern():
182 return os
.path
.join(tempfile
.gettempdir(), 'git-cola-%s-*' % os
.getpid())
185 def tmp_filename(label
):
186 prefix
= 'git-cola-%s-' % (os
.getpid())
187 suffix
= '-%s' % label
.replace('/', '-').replace('\\', '-')
188 fd
, path
= tempfile
.mkstemp(suffix
, prefix
)
194 """Is this a linux machine?"""
195 return sys
.platform
.startswith('linux')
200 return os
.path
.exists('/usr/bin/apt-get')
204 """Return True on OSX."""
205 return sys
.platform
== 'darwin'
209 """Return True on win32"""
210 return sys
.platform
== 'win32' or sys
.platform
== 'cygwin'
213 def expandpath(path
):
214 """Expand ~user/ and environment $variables"""
215 path
= os
.path
.expandvars(path
)
216 if path
.startswith('~'):
217 path
= os
.path
.expanduser(path
)
222 """Operate on a collection of objects as a single unit"""
224 def __init__(self
, *members
):
225 self
._members
= members
227 def __getattr__(self
, name
):
228 """Return a function that relays calls to the group"""
229 def relay(*args
, **kwargs
):
230 for member
in self
._members
:
231 method
= getattr(member
, name
)
232 method(*args
, **kwargs
)
233 setattr(self
, name
, relay
)
238 """Wrap an object and override attributes"""
240 def __init__(self
, obj
, **overrides
):
242 for k
, v
in overrides
.items():
245 def __getattr__(self
, name
):
246 return getattr(self
._obj
, name
)