models.main: Do not return cached values in everything()
[git-cola.git] / cola / utils.py
blob261f3b52b95df3badc51ba6de8f7c7ad115afb44
1 # Copyright (c) 2008 David Aguilar
2 """This module provides miscellaneous utility functions."""
4 import os
5 import re
6 import sys
7 import errno
8 import platform
9 import subprocess
10 import hashlib
11 import mimetypes
13 from glob import glob
14 from cStringIO import StringIO
16 from cola import git
17 from cola import core
18 from cola import resources
19 from cola.git import shell_quote
21 KNOWN_FILE_MIME_TYPES = {
22 'text': 'script.png',
23 'image': 'image.png',
24 'python': 'script.png',
25 'ruby': 'script.png',
26 'shell': 'script.png',
27 'perl': 'script.png',
28 'octet': 'binary.png',
31 KNOWN_FILE_EXTENSION = {
32 '.java': 'script.png',
33 '.groovy': 'script.png',
34 '.cpp': 'script.png',
35 '.c': 'script.png',
36 '.h': 'script.png',
37 '.cxx': 'script.png',
41 def add_parents(path_entry_set):
42 """Iterate over each item in the set and add its parent directories."""
43 for path in list(path_entry_set):
44 while '//' in path:
45 path = path.replace('//', '/')
46 if path not in path_entry_set:
47 path_entry_set.add(path)
48 if '/' in path:
49 parent_dir = dirname(path)
50 while parent_dir and parent_dir not in path_entry_set:
51 path_entry_set.add(parent_dir)
52 parent_dir = dirname(parent_dir)
53 return path_entry_set
56 def run_cmd(command):
57 """
58 Run arguments as a command and return output.
60 >>> run_cmd(["echo", "hello", "world"])
61 'hello world'
63 """
64 return git.Git.execute(command)
67 def qm_for_locale(locale):
68 """Returns the .qm file for a particular $LANG values."""
69 regex = re.compile(r'([^\.])+\..*$')
70 match = regex.match(locale)
71 if match:
72 locale = match.group(1)
73 return resources.qm(locale.split('_')[0])
76 def ident_file_type(filename):
77 """Returns an icon based on the contents of filename."""
78 if os.path.exists(filename):
79 filemimetype = mimetypes.guess_type(filename)
80 if filemimetype[0] != None:
81 for filetype, iconname in KNOWN_FILE_MIME_TYPES.iteritems():
82 if filetype in filemimetype[0].lower():
83 return iconname
84 filename = filename.lower()
85 for fileext, iconname in KNOWN_FILE_EXTENSION.iteritems():
86 if filename.endswith(fileext):
87 return iconname
88 return 'generic.png'
89 else:
90 return 'removed.png'
91 # Fallback for modified files of an unknown type
92 return 'generic.png'
95 def file_icon(filename):
96 """
97 Returns the full path to an icon file corresponding to
98 filename"s contents.
99 """
100 return resources.icon(ident_file_type(filename))
103 def fork(args):
104 """Launches a command in the background."""
105 args = tuple([core.encode(a) for a in args])
106 if os.name in ('nt', 'dos'):
107 for path in os.environ['PATH'].split(os.pathsep):
108 filenames = (os.path.join(path, args[0]),
109 os.path.join(path, args[0]) + ".exe")
110 for filename in filenames:
111 if os.path.exists(filename):
112 try:
113 return os.spawnv(os.P_NOWAIT, filename, args)
114 except os.error:
115 pass
116 raise IOError('cannot find executable: %s' % args[0])
117 else:
118 argv = map(shell_quote, args)
119 return os.system(' '.join(argv) + '&')
122 def sublist(a,b):
123 """Subtracts list b from list a and returns the resulting list."""
124 # conceptually, c = a - b
125 c = []
126 for item in a:
127 if item not in b:
128 c.append(item)
129 return c
132 __grep_cache = {}
133 def grep(pattern, items, squash=True):
134 """Greps a list for items that match a pattern and return a list of
135 matching items. If only one item matches, return just that item.
137 isdict = type(items) is dict
138 if pattern in __grep_cache:
139 regex = __grep_cache[pattern]
140 else:
141 regex = __grep_cache[pattern] = re.compile(pattern)
142 matched = []
143 matchdict = {}
144 for item in items:
145 match = regex.match(item)
146 if not match:
147 continue
148 groups = match.groups()
149 if not groups:
150 subitems = match.group(0)
151 else:
152 if len(groups) == 1:
153 subitems = groups[0]
154 else:
155 subitems = list(groups)
156 if isdict:
157 matchdict[item] = items[item]
158 else:
159 matched.append(subitems)
161 if isdict:
162 return matchdict
163 else:
164 if squash and len(matched) == 1:
165 return matched[0]
166 else:
167 return matched
170 def basename(path):
172 An os.path.basename() implementation that always uses '/'
174 Avoid os.path.basename because git's output always
175 uses '/' regardless of platform.
178 return path.rsplit('/', 1)[-1]
181 def dirname(path):
183 An os.path.dirname() implementation that always uses '/'
185 Avoid os.path.dirname because git's output always
186 uses '/' regardless of platform.
189 while '//' in path:
190 path = path.replace('//', '/')
191 path_dirname = path.rsplit('/', 1)[0]
192 if path_dirname == path:
193 return ''
194 return path.rsplit('/', 1)[0]
197 def slurp(path):
198 """Slurps a filepath into a string."""
199 fh = open(path)
200 slushy = core.read_nointr(fh)
201 fh.close()
202 return core.decode(slushy)
205 def write(path, contents):
206 """Writes a string to a file."""
207 fh = open(path, 'w')
208 core.write_nointr(fh, core.encode(contents))
209 fh.close()
211 def strip_prefix(prefix, string):
212 """Return string, without the prefix. Blow up if string doesn't
213 start with prefix."""
214 assert string.startswith(prefix)
215 return string[len(prefix):]
217 def sanitize(s):
218 """Removes shell metacharacters from a string."""
219 for c in """ \t!@#$%^&*()\\;,<>"'[]{}~|""":
220 s = s.replace(c, '_')
221 return s
223 def is_linux():
224 """Is this a linux machine?"""
225 while True:
226 try:
227 return platform.system() == 'Linux'
228 except IOError, e:
229 if e.errno == errno.EINTR:
230 continue
231 raise e
233 def is_debian():
234 """Is it debian?"""
235 return os.path.exists('/usr/bin/apt-get')
238 def is_darwin():
239 """Return True on OSX."""
240 while True:
241 try:
242 p = platform.platform()
243 break
244 except IOError, e:
245 if e.errno == errno.EINTR:
246 continue
247 raise e
248 p = p.lower()
249 return 'macintosh' in p or 'darwin' in p
252 def is_broken():
253 """Is it windows or mac? (e.g. is running git-mergetool non-trivial?)"""
254 if is_darwin():
255 return True
256 while True:
257 try:
258 return platform.system() == 'Windows'
259 except IOError, e:
260 if e.errno == errno.EINTR:
261 continue
262 raise e
265 def checksum(path):
266 """Return a cheap md5 hexdigest for a path."""
267 contents = slurp(path)
268 md5 = hashlib.new('md5')
269 md5.add(contents)
270 return md5.hexdigest()
273 def quote_repopath(repopath):
274 """Quote a path for nt/dos only."""
275 if os.name in ('nt', 'dos'):
276 repopath = '"%s"' % repopath
277 return repopath