##// END OF EJS Templates
Update util.py docstrings, fix walk test
mpm@selenic.com -
r1082:ce96e316 default
parent child Browse files
Show More
@@ -1,290 +1,327
1 # util.py - utility functions and platform specfic implementations
2 #
3 # Copyright 2005 K. Thananchayan <thananck@yahoo.com>
4 #
5 # This software may be used and distributed according to the terms
6 # of the GNU General Public License, incorporated herein by reference.
1 """
2 util.py - Mercurial utility functions and platform specfic implementations
3
4 Copyright 2005 K. Thananchayan <thananck@yahoo.com>
5
6 This software may be used and distributed according to the terms
7 of the GNU General Public License, incorporated herein by reference.
8
9 This contains helper routines that are independent of the SCM core and hide
10 platform-specific details from the core.
11 """
7 12
8 13 import os, errno
9 14 from demandload import *
10 15 demandload(globals(), "re")
11 16
12 17 def binary(s):
18 """return true if a string is binary data using diff's heuristic"""
13 19 if s and '\0' in s[:4096]:
14 20 return True
15 21 return False
16 22
17 23 def unique(g):
24 """return the uniq elements of iterable g"""
18 25 seen = {}
19 26 for f in g:
20 27 if f not in seen:
21 28 seen[f] = 1
22 29 yield f
23 30
24 31 class Abort(Exception):
25 32 """Raised if a command needs to print an error and exit."""
26 33
27 34 def always(fn): return True
28 35 def never(fn): return False
29 36
30 37 def globre(pat, head='^', tail='$'):
31 38 "convert a glob pattern into a regexp"
32 39 i, n = 0, len(pat)
33 40 res = ''
34 41 group = False
35 42 def peek(): return i < n and pat[i]
36 43 while i < n:
37 44 c = pat[i]
38 45 i = i+1
39 46 if c == '*':
40 47 if peek() == '*':
41 48 i += 1
42 49 res += '.*'
43 50 else:
44 51 res += '[^/]*'
45 52 elif c == '?':
46 53 res += '.'
47 54 elif c == '[':
48 55 j = i
49 56 if j < n and pat[j] in '!]':
50 57 j += 1
51 58 while j < n and pat[j] != ']':
52 59 j += 1
53 60 if j >= n:
54 61 res += '\\['
55 62 else:
56 63 stuff = pat[i:j].replace('\\','\\\\')
57 64 i = j + 1
58 65 if stuff[0] == '!':
59 66 stuff = '^' + stuff[1:]
60 67 elif stuff[0] == '^':
61 68 stuff = '\\' + stuff
62 69 res = '%s[%s]' % (res, stuff)
63 70 elif c == '{':
64 71 group = True
65 72 res += '(?:'
66 73 elif c == '}' and group:
67 74 res += ')'
68 75 group = False
69 76 elif c == ',' and group:
70 77 res += '|'
71 78 else:
72 79 res += re.escape(c)
73 80 return head + res + tail
74 81
75 82 _globchars = {'[': 1, '{': 1, '*': 1, '?': 1}
76 83
77 84 def pathto(n1, n2):
78 85 '''return the relative path from one place to another.
79 86 this returns a path in the form used by the local filesystem, not hg.'''
80 87 if not n1: return localpath(n2)
81 88 a, b = n1.split('/'), n2.split('/')
82 89 a.reverse(), b.reverse()
83 90 while a and b and a[-1] == b[-1]:
84 91 a.pop(), b.pop()
85 92 b.reverse()
86 93 return os.sep.join((['..'] * len(a)) + b)
87 94
88 95 def canonpath(root, cwd, myname):
96 """return the canonical path of myname, given cwd and root"""
89 97 rootsep = root + os.sep
90 98 name = myname
91 99 if not name.startswith(os.sep):
92 100 name = os.path.join(root, cwd, name)
93 101 name = os.path.normpath(name)
94 102 if name.startswith(rootsep):
95 103 return pconvert(name[len(rootsep):])
96 104 elif name == root:
97 105 return ''
98 106 else:
99 107 raise Abort('%s not under root' % myname)
100 108
101 109 def matcher(canonroot, cwd, names, inc, exc, head=''):
110 """build a function to match a set of file patterns
111
112 arguments:
113 canonroot - the canonical root of the tree you're matching against
114 cwd - the current working directory, if relevant
115 names - patterns to find
116 inc - patterns to include
117 exc - patterns to exclude
118 head - a regex to prepend to patterns to control whether a match is rooted
119
120 a pattern is one of:
121 're:<regex>'
122 'glob:<shellglob>'
123 'path:<explicit path>'
124 'relpath:<relative path>'
125 '<relative path>'
126
127 returns:
128 a 3-tuple containing
129 - list of explicit non-pattern names passed in
130 - a bool match(filename) function
131 - a bool indicating if any patterns were passed in
132
133 todo:
134 make head regex a rooted bool
135 """
136
102 137 def patkind(name):
103 138 for prefix in 're:', 'glob:', 'path:', 'relpath:':
104 139 if name.startswith(prefix): return name.split(':', 1)
105 140 for c in name:
106 141 if c in _globchars: return 'glob', name
107 142 return 'relpath', name
108 143
109 144 def regex(kind, name, tail):
110 145 '''convert a pattern into a regular expression'''
111 146 if kind == 're':
112 147 return name
113 148 elif kind == 'path':
114 149 return '^' + re.escape(name) + '(?:/|$)'
115 150 elif kind == 'relpath':
116 151 return head + re.escape(name) + tail
117 152 return head + globre(name, '', tail)
118 153
119 154 def matchfn(pats, tail):
120 155 """build a matching function from a set of patterns"""
121 156 if pats:
122 157 pat = '(?:%s)' % '|'.join([regex(k, p, tail) for (k, p) in pats])
123 158 return re.compile(pat).match
124 159
125 160 def globprefix(pat):
126 161 '''return the non-glob prefix of a path, e.g. foo/* -> foo'''
127 162 root = []
128 163 for p in pat.split(os.sep):
129 164 if patkind(p)[0] == 'glob': break
130 165 root.append(p)
131 166 return '/'.join(root)
132 167
133 168 pats = []
134 169 files = []
135 170 roots = []
136 171 for kind, name in map(patkind, names):
137 172 if kind in ('glob', 'relpath'):
138 173 name = canonpath(canonroot, cwd, name)
139 174 if name == '':
140 175 kind, name = 'glob', '**'
141 176 if kind in ('glob', 'path', 're'):
142 177 pats.append((kind, name))
143 178 if kind == 'glob':
144 179 root = globprefix(name)
145 180 if root: roots.append(root)
146 181 elif kind == 'relpath':
147 182 files.append((kind, name))
148 183 roots.append(name)
149 184
150 185 patmatch = matchfn(pats, '$') or always
151 186 filematch = matchfn(files, '(?:/|$)') or always
152 187 incmatch = always
153 188 if inc:
154 189 incmatch = matchfn(map(patkind, inc), '(?:/|$)')
155 190 excmatch = lambda fn: False
156 191 if exc:
157 192 excmatch = matchfn(map(patkind, exc), '(?:/|$)')
158 193
159 194 return (roots,
160 195 lambda fn: (incmatch(fn) and not excmatch(fn) and
161 196 (fn.endswith('/') or
162 197 (not pats and not files) or
163 198 (pats and patmatch(fn)) or
164 199 (files and filematch(fn)))),
165 200 (inc or exc or (pats and pats != [('glob', '**')])) and True)
166 201
167 202 def system(cmd, errprefix=None):
168 203 """execute a shell command that must succeed"""
169 204 rc = os.system(cmd)
170 205 if rc:
171 206 errmsg = "%s %s" % (os.path.basename(cmd.split(None, 1)[0]),
172 207 explain_exit(rc)[0])
173 208 if errprefix:
174 209 errmsg = "%s: %s" % (errprefix, errmsg)
175 210 raise Abort(errmsg)
176 211
177 212 def rename(src, dst):
213 """forcibly rename a file"""
178 214 try:
179 215 os.rename(src, dst)
180 216 except:
181 217 os.unlink(dst)
182 218 os.rename(src, dst)
183 219
184 220 def copytree(src, dst, copyfile):
185 221 """Copy a directory tree, files are copied using 'copyfile'."""
186 222 names = os.listdir(src)
187 223 os.mkdir(dst)
188 224
189 225 for name in names:
190 226 srcname = os.path.join(src, name)
191 227 dstname = os.path.join(dst, name)
192 228 if os.path.isdir(srcname):
193 229 copytree(srcname, dstname, copyfile)
194 230 elif os.path.isfile(srcname):
195 231 copyfile(srcname, dstname)
196 232 else:
197 233 pass
198 234
199 235 def _makelock_file(info, pathname):
200 236 ld = os.open(pathname, os.O_CREAT | os.O_WRONLY | os.O_EXCL)
201 237 os.write(ld, info)
202 238 os.close(ld)
203 239
204 240 def _readlock_file(pathname):
205 241 return file(pathname).read()
206 242
207 # Platfor specific varients
243 # Platform specific variants
208 244 if os.name == 'nt':
209 245 nulldev = 'NUL:'
210 246
211 247 def is_exec(f, last):
212 248 return last
213 249
214 250 def set_exec(f, mode):
215 251 pass
216 252
217 253 def pconvert(path):
218 254 return path.replace("\\", "/")
219 255
220 256 def localpath(path):
221 257 return path.replace('/', '\\')
222 258
223 259 def normpath(path):
224 260 return pconvert(os.path.normpath(path))
225 261
226 262 makelock = _makelock_file
227 263 readlock = _readlock_file
228 264
229 265 def explain_exit(code):
230 266 return "exited with status %d" % code, code
231 267
232 268 else:
233 269 nulldev = '/dev/null'
234 270
235 271 def is_exec(f, last):
272 """check whether a file is executable"""
236 273 return (os.stat(f).st_mode & 0100 != 0)
237 274
238 275 def set_exec(f, mode):
239 276 s = os.stat(f).st_mode
240 277 if (s & 0100 != 0) == mode:
241 278 return
242 279 if mode:
243 280 # Turn on +x for every +r bit when making a file executable
244 281 # and obey umask.
245 282 umask = os.umask(0)
246 283 os.umask(umask)
247 284 os.chmod(f, s | (s & 0444) >> 2 & ~umask)
248 285 else:
249 286 os.chmod(f, s & 0666)
250 287
251 288 def pconvert(path):
252 289 return path
253 290
254 291 def localpath(path):
255 292 return path
256 293
257 294 normpath = os.path.normpath
258 295
259 296 def makelock(info, pathname):
260 297 try:
261 298 os.symlink(info, pathname)
262 299 except OSError, why:
263 300 if why.errno == errno.EEXIST:
264 301 raise
265 302 else:
266 303 _makelock_file(info, pathname)
267 304
268 305 def readlock(pathname):
269 306 try:
270 307 return os.readlink(pathname)
271 308 except OSError, why:
272 309 if why.errno == errno.EINVAL:
273 310 return _readlock_file(pathname)
274 311 else:
275 312 raise
276 313
277 314 def explain_exit(code):
278 315 """return a 2-tuple (desc, code) describing a process's status"""
279 316 if os.name == 'nt': # os.WIFxx is not supported on windows
280 317 return "aborted with error." , -1
281 318 if os.WIFEXITED(code):
282 319 val = os.WEXITSTATUS(code)
283 320 return "exited with status %d" % val, val
284 321 elif os.WIFSIGNALED(code):
285 322 val = os.WTERMSIG(code)
286 323 return "killed by signal %d" % val, val
287 324 elif os.WIFSTOPPED(code):
288 325 val = os.WSTOPSIG(code)
289 326 return "stopped by signal %d" % val, val
290 327 raise ValueError("invalid exit code")
@@ -1,87 +1,87
1 1 adding fennel
2 2 adding fenugreek
3 3 adding fiddlehead
4 4 adding glob:glob
5 5 adding beans/black
6 6 adding beans/borlotti
7 7 adding beans/kidney
8 8 adding beans/navy
9 9 adding beans/pinto
10 10 adding beans/turtle
11 11 adding mammals/skunk
12 12 adding mammals/Procyonidae/cacomistle
13 13 adding mammals/Procyonidae/coatimundi
14 14 adding mammals/Procyonidae/raccoon
15 15 f fennel fennel
16 16 f fenugreek fenugreek
17 17 f fiddlehead fiddlehead
18 18 f glob:glob glob:glob
19 19 f beans/black beans/black
20 20 f beans/borlotti beans/borlotti
21 21 f beans/kidney beans/kidney
22 22 f beans/navy beans/navy
23 23 f beans/pinto beans/pinto
24 24 f beans/turtle beans/turtle
25 25 f mammals/skunk mammals/skunk
26 26 f mammals/Procyonidae/cacomistle mammals/Procyonidae/cacomistle
27 27 f mammals/Procyonidae/coatimundi mammals/Procyonidae/coatimundi
28 28 f mammals/Procyonidae/raccoon mammals/Procyonidae/raccoon
29 29 f mammals/skunk skunk
30 30 f mammals/Procyonidae/cacomistle Procyonidae/cacomistle
31 31 f mammals/Procyonidae/coatimundi Procyonidae/coatimundi
32 32 f mammals/Procyonidae/raccoon Procyonidae/raccoon
33 33 f mammals/Procyonidae/cacomistle Procyonidae/cacomistle
34 34 f mammals/Procyonidae/coatimundi Procyonidae/coatimundi
35 35 f mammals/Procyonidae/raccoon Procyonidae/raccoon
36 36 f mammals/Procyonidae/cacomistle cacomistle
37 37 f mammals/Procyonidae/coatimundi coatimundi
38 38 f mammals/Procyonidae/raccoon raccoon
39 39 f mammals/skunk ../skunk
40 40 f mammals/Procyonidae/cacomistle cacomistle
41 41 f mammals/Procyonidae/coatimundi coatimundi
42 42 f mammals/Procyonidae/raccoon raccoon
43 43 f beans/black ../beans/black
44 44 f beans/borlotti ../beans/borlotti
45 45 f beans/kidney ../beans/kidney
46 46 f beans/navy ../beans/navy
47 47 f beans/pinto ../beans/pinto
48 48 f beans/turtle ../beans/turtle
49 49 f mammals/skunk skunk
50 50 f mammals/Procyonidae/cacomistle Procyonidae/cacomistle
51 51 f mammals/Procyonidae/coatimundi Procyonidae/coatimundi
52 52 f mammals/Procyonidae/raccoon Procyonidae/raccoon
53 53 f beans/black beans/black
54 54 f beans/borlotti beans/borlotti
55 55 f beans/kidney beans/kidney
56 56 f beans/navy beans/navy
57 57 f beans/pinto beans/pinto
58 58 f beans/turtle beans/turtle
59 59 f beans/black beans/black
60 60 f beans/borlotti beans/borlotti
61 61 f mammals/skunk mammals/skunk
62 62 f mammals/skunk mammals/skunk
63 63 f mammals/Procyonidae/cacomistle mammals/Procyonidae/cacomistle
64 64 f mammals/Procyonidae/coatimundi mammals/Procyonidae/coatimundi
65 65 f mammals/Procyonidae/raccoon mammals/Procyonidae/raccoon
66 abort: .. not under repository root
67 abort: beans/../.. not under repository root
66 abort: .. not under root
67 abort: beans/../.. not under root
68 68 f fennel fennel
69 69 f fenugreek fenugreek
70 70 f fiddlehead fiddlehead
71 71 f glob:glob glob:glob
72 72 f fenugreek fenugreek
73 73 f glob:glob glob:glob
74 74 f beans/black beans/black
75 75 f mammals/skunk mammals/skunk
76 76 f beans/black beans/black
77 77 f beans/black beans/black
78 78 f beans/borlotti beans/borlotti
79 79 f beans/kidney beans/kidney
80 80 f beans/navy beans/navy
81 81 f beans/pinto beans/pinto
82 82 f beans/turtle beans/turtle
83 83 NOEXIST: No such file or directory
84 84 fifo: unsupported file type (type is fifo)
85 85 m fenugreek fenugreek exact
86 86 m fenugreek fenugreek exact
87 87 f new new exact
General Comments 0
You need to be logged in to leave comments. Login now