##// END OF EJS Templates
merge revlogng with mpm tip
Chris Mason -
r2085:f71e9656 merge default
parent child Browse files
Show More
@@ -0,0 +1,171 b''
1 # util_win32.py - utility functions that use win32 API
2 #
3 # Copyright 2005 Matt Mackall <mpm@selenic.com>
4 # Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
5 #
6 # This software may be used and distributed according to the terms of
7 # the GNU General Public License, incorporated herein by reference.
8
9 # Mark Hammond's win32all package allows better functionality on
10 # Windows. this module overrides definitions in util.py. if not
11 # available, import of this module will fail, and generic code will be
12 # used.
13
14 import win32api
15
16 from demandload import *
17 from i18n import gettext as _
18 demandload(globals(), 'errno os pywintypes win32con win32file win32process')
19 demandload(globals(), 'winerror')
20
21 class WinError(OSError):
22 winerror_map = {
23 winerror.ERROR_ACCESS_DENIED: errno.EACCES,
24 winerror.ERROR_ACCOUNT_DISABLED: errno.EACCES,
25 winerror.ERROR_ACCOUNT_RESTRICTION: errno.EACCES,
26 winerror.ERROR_ALREADY_ASSIGNED: errno.EBUSY,
27 winerror.ERROR_ALREADY_EXISTS: errno.EEXIST,
28 winerror.ERROR_ARITHMETIC_OVERFLOW: errno.ERANGE,
29 winerror.ERROR_BAD_COMMAND: errno.EIO,
30 winerror.ERROR_BAD_DEVICE: errno.ENODEV,
31 winerror.ERROR_BAD_DRIVER_LEVEL: errno.ENXIO,
32 winerror.ERROR_BAD_EXE_FORMAT: errno.ENOEXEC,
33 winerror.ERROR_BAD_FORMAT: errno.ENOEXEC,
34 winerror.ERROR_BAD_LENGTH: errno.EINVAL,
35 winerror.ERROR_BAD_PATHNAME: errno.ENOENT,
36 winerror.ERROR_BAD_PIPE: errno.EPIPE,
37 winerror.ERROR_BAD_UNIT: errno.ENODEV,
38 winerror.ERROR_BAD_USERNAME: errno.EINVAL,
39 winerror.ERROR_BROKEN_PIPE: errno.EPIPE,
40 winerror.ERROR_BUFFER_OVERFLOW: errno.ENAMETOOLONG,
41 winerror.ERROR_BUSY: errno.EBUSY,
42 winerror.ERROR_BUSY_DRIVE: errno.EBUSY,
43 winerror.ERROR_CALL_NOT_IMPLEMENTED: errno.ENOSYS,
44 winerror.ERROR_CANNOT_MAKE: errno.EACCES,
45 winerror.ERROR_CANTOPEN: errno.EIO,
46 winerror.ERROR_CANTREAD: errno.EIO,
47 winerror.ERROR_CANTWRITE: errno.EIO,
48 winerror.ERROR_CRC: errno.EIO,
49 winerror.ERROR_CURRENT_DIRECTORY: errno.EACCES,
50 winerror.ERROR_DEVICE_IN_USE: errno.EBUSY,
51 winerror.ERROR_DEV_NOT_EXIST: errno.ENODEV,
52 winerror.ERROR_DIRECTORY: errno.EINVAL,
53 winerror.ERROR_DIR_NOT_EMPTY: errno.ENOTEMPTY,
54 winerror.ERROR_DISK_CHANGE: errno.EIO,
55 winerror.ERROR_DISK_FULL: errno.ENOSPC,
56 winerror.ERROR_DRIVE_LOCKED: errno.EBUSY,
57 winerror.ERROR_ENVVAR_NOT_FOUND: errno.EINVAL,
58 winerror.ERROR_EXE_MARKED_INVALID: errno.ENOEXEC,
59 winerror.ERROR_FILENAME_EXCED_RANGE: errno.ENAMETOOLONG,
60 winerror.ERROR_FILE_EXISTS: errno.EEXIST,
61 winerror.ERROR_FILE_INVALID: errno.ENODEV,
62 winerror.ERROR_FILE_NOT_FOUND: errno.ENOENT,
63 winerror.ERROR_GEN_FAILURE: errno.EIO,
64 winerror.ERROR_HANDLE_DISK_FULL: errno.ENOSPC,
65 winerror.ERROR_INSUFFICIENT_BUFFER: errno.ENOMEM,
66 winerror.ERROR_INVALID_ACCESS: errno.EACCES,
67 winerror.ERROR_INVALID_ADDRESS: errno.EFAULT,
68 winerror.ERROR_INVALID_BLOCK: errno.EFAULT,
69 winerror.ERROR_INVALID_DATA: errno.EINVAL,
70 winerror.ERROR_INVALID_DRIVE: errno.ENODEV,
71 winerror.ERROR_INVALID_EXE_SIGNATURE: errno.ENOEXEC,
72 winerror.ERROR_INVALID_FLAGS: errno.EINVAL,
73 winerror.ERROR_INVALID_FUNCTION: errno.ENOSYS,
74 winerror.ERROR_INVALID_HANDLE: errno.EBADF,
75 winerror.ERROR_INVALID_LOGON_HOURS: errno.EACCES,
76 winerror.ERROR_INVALID_NAME: errno.EINVAL,
77 winerror.ERROR_INVALID_OWNER: errno.EINVAL,
78 winerror.ERROR_INVALID_PARAMETER: errno.EINVAL,
79 winerror.ERROR_INVALID_PASSWORD: errno.EPERM,
80 winerror.ERROR_INVALID_PRIMARY_GROUP: errno.EINVAL,
81 winerror.ERROR_INVALID_SIGNAL_NUMBER: errno.EINVAL,
82 winerror.ERROR_INVALID_TARGET_HANDLE: errno.EIO,
83 winerror.ERROR_INVALID_WORKSTATION: errno.EACCES,
84 winerror.ERROR_IO_DEVICE: errno.EIO,
85 winerror.ERROR_IO_INCOMPLETE: errno.EINTR,
86 winerror.ERROR_LOCKED: errno.EBUSY,
87 winerror.ERROR_LOCK_VIOLATION: errno.EACCES,
88 winerror.ERROR_LOGON_FAILURE: errno.EACCES,
89 winerror.ERROR_MAPPED_ALIGNMENT: errno.EINVAL,
90 winerror.ERROR_META_EXPANSION_TOO_LONG: errno.E2BIG,
91 winerror.ERROR_MORE_DATA: errno.EPIPE,
92 winerror.ERROR_NEGATIVE_SEEK: errno.ESPIPE,
93 winerror.ERROR_NOACCESS: errno.EFAULT,
94 winerror.ERROR_NONE_MAPPED: errno.EINVAL,
95 winerror.ERROR_NOT_ENOUGH_MEMORY: errno.ENOMEM,
96 winerror.ERROR_NOT_READY: errno.EAGAIN,
97 winerror.ERROR_NOT_SAME_DEVICE: errno.EXDEV,
98 winerror.ERROR_NO_DATA: errno.EPIPE,
99 winerror.ERROR_NO_MORE_SEARCH_HANDLES: errno.EIO,
100 winerror.ERROR_NO_PROC_SLOTS: errno.EAGAIN,
101 winerror.ERROR_NO_SUCH_PRIVILEGE: errno.EACCES,
102 winerror.ERROR_OPEN_FAILED: errno.EIO,
103 winerror.ERROR_OPEN_FILES: errno.EBUSY,
104 winerror.ERROR_OPERATION_ABORTED: errno.EINTR,
105 winerror.ERROR_OUTOFMEMORY: errno.ENOMEM,
106 winerror.ERROR_PASSWORD_EXPIRED: errno.EACCES,
107 winerror.ERROR_PATH_BUSY: errno.EBUSY,
108 winerror.ERROR_PATH_NOT_FOUND: errno.ENOTDIR,
109 winerror.ERROR_PIPE_BUSY: errno.EBUSY,
110 winerror.ERROR_PIPE_CONNECTED: errno.EPIPE,
111 winerror.ERROR_PIPE_LISTENING: errno.EPIPE,
112 winerror.ERROR_PIPE_NOT_CONNECTED: errno.EPIPE,
113 winerror.ERROR_PRIVILEGE_NOT_HELD: errno.EACCES,
114 winerror.ERROR_READ_FAULT: errno.EIO,
115 winerror.ERROR_SEEK: errno.EIO,
116 winerror.ERROR_SEEK_ON_DEVICE: errno.ESPIPE,
117 winerror.ERROR_SHARING_BUFFER_EXCEEDED: errno.ENFILE,
118 winerror.ERROR_SHARING_VIOLATION: errno.EACCES,
119 winerror.ERROR_STACK_OVERFLOW: errno.ENOMEM,
120 winerror.ERROR_SWAPERROR: errno.ENOENT,
121 winerror.ERROR_TOO_MANY_MODULES: errno.EMFILE,
122 winerror.ERROR_TOO_MANY_OPEN_FILES: errno.EMFILE,
123 winerror.ERROR_UNRECOGNIZED_MEDIA: errno.ENXIO,
124 winerror.ERROR_UNRECOGNIZED_VOLUME: errno.ENODEV,
125 winerror.ERROR_WAIT_NO_CHILDREN: errno.ECHILD,
126 winerror.ERROR_WRITE_FAULT: errno.EIO,
127 winerror.ERROR_WRITE_PROTECT: errno.EROFS,
128 }
129
130 def __init__(self, err):
131 self.win_errno, self.win_function, self.win_strerror = err
132 OSError.__init__(self, self.winerror_map.get(self.win_errno, 0),
133 self.win_strerror)
134
135 def os_link(src, dst):
136 # NB will only succeed on NTFS
137 try:
138 win32file.CreateHardLink(dst, src)
139 except pywintypes.error, details:
140 raise WinError(details)
141
142 def nlinks(pathname):
143 """Return number of hardlinks for the given file."""
144 try:
145 fh = win32file.CreateFile(pathname,
146 win32file.GENERIC_READ, win32file.FILE_SHARE_READ,
147 None, win32file.OPEN_EXISTING, 0, None)
148 res = win32file.GetFileInformationByHandle(fh)
149 fh.Close()
150 return res[7]
151 except pywintypes.error:
152 return os.stat(pathname).st_nlink
153
154 def testpid(pid):
155 '''return True if pid is still running or unable to
156 determine, False otherwise'''
157 try:
158 handle = win32api.OpenProcess(
159 win32con.PROCESS_QUERY_INFORMATION, False, pid)
160 if handle:
161 status = win32process.GetExitCodeProcess(handle)
162 return status == win32con.STILL_ACTIVE
163 except pywintypes.error, details:
164 return details[0] != winerror.ERROR_INVALID_PARAMETER
165 return True
166
167 def system_rcpath():
168 '''return default os-specific hgrc search path'''
169 proc = win32api.GetCurrentProcess()
170 filename = win32process.GetModuleFileNameEx(proc, 0)
171 return [os.path.join(os.path.dirname(filename), 'mercurial.ini')]
This diff has been collapsed as it changes many lines, (890 lines changed) Show them Hide them
@@ -0,0 +1,890 b''
1 #!/usr/bin/python
2 #
3 # Perforce Defect Tracking Integration Project
4 # <http://www.ravenbrook.com/project/p4dti/>
5 #
6 # COVERAGE.PY -- COVERAGE TESTING
7 #
8 # Gareth Rees, Ravenbrook Limited, 2001-12-04
9 # Ned Batchelder, 2004-12-12
10 # http://nedbatchelder.com/code/modules/coverage.html
11 #
12 #
13 # 1. INTRODUCTION
14 #
15 # This module provides coverage testing for Python code.
16 #
17 # The intended readership is all Python developers.
18 #
19 # This document is not confidential.
20 #
21 # See [GDR 2001-12-04a] for the command-line interface, programmatic
22 # interface and limitations. See [GDR 2001-12-04b] for requirements and
23 # design.
24
25 """Usage:
26
27 coverage.py -x MODULE.py [ARG1 ARG2 ...]
28 Execute module, passing the given command-line arguments, collecting
29 coverage data.
30
31 coverage.py -e
32 Erase collected coverage data.
33
34 coverage.py -r [-m] [-o dir1,dir2,...] FILE1 FILE2 ...
35 Report on the statement coverage for the given files. With the -m
36 option, show line numbers of the statements that weren't executed.
37
38 coverage.py -a [-d dir] [-o dir1,dir2,...] FILE1 FILE2 ...
39 Make annotated copies of the given files, marking statements that
40 are executed with > and statements that are missed with !. With
41 the -d option, make the copies in that directory. Without the -d
42 option, make each copy in the same directory as the original.
43
44 -o dir,dir2,...
45 Omit reporting or annotating files when their filename path starts with
46 a directory listed in the omit list.
47 e.g. python coverage.py -i -r -o c:\python23,lib\enthought\traits
48
49 Coverage data is saved in the file .coverage by default. Set the
50 COVERAGE_FILE environment variable to save it somewhere else."""
51
52 __version__ = "2.5.20051204" # see detailed history at the end of this file.
53
54 import compiler
55 import compiler.visitor
56 import os
57 import re
58 import string
59 import sys
60 import threading
61 import types
62
63 # 2. IMPLEMENTATION
64 #
65 # This uses the "singleton" pattern.
66 #
67 # The word "morf" means a module object (from which the source file can
68 # be deduced by suitable manipulation of the __file__ attribute) or a
69 # filename.
70 #
71 # When we generate a coverage report we have to canonicalize every
72 # filename in the coverage dictionary just in case it refers to the
73 # module we are reporting on. It seems a shame to throw away this
74 # information so the data in the coverage dictionary is transferred to
75 # the 'cexecuted' dictionary under the canonical filenames.
76 #
77 # The coverage dictionary is called "c" and the trace function "t". The
78 # reason for these short names is that Python looks up variables by name
79 # at runtime and so execution time depends on the length of variables!
80 # In the bottleneck of this application it's appropriate to abbreviate
81 # names to increase speed.
82
83 class StatementFindingAstVisitor(compiler.visitor.ASTVisitor):
84 def __init__(self, statements, excluded, suite_spots):
85 compiler.visitor.ASTVisitor.__init__(self)
86 self.statements = statements
87 self.excluded = excluded
88 self.suite_spots = suite_spots
89 self.excluding_suite = 0
90
91 def doRecursive(self, node):
92 self.recordNodeLine(node)
93 for n in node.getChildNodes():
94 self.dispatch(n)
95
96 visitStmt = visitModule = doRecursive
97
98 def doCode(self, node):
99 if hasattr(node, 'decorators') and node.decorators:
100 self.dispatch(node.decorators)
101 self.doSuite(node, node.code)
102
103 visitFunction = visitClass = doCode
104
105 def getFirstLine(self, node):
106 # Find the first line in the tree node.
107 lineno = node.lineno
108 for n in node.getChildNodes():
109 f = self.getFirstLine(n)
110 if lineno and f:
111 lineno = min(lineno, f)
112 else:
113 lineno = lineno or f
114 return lineno
115
116 def getLastLine(self, node):
117 # Find the first line in the tree node.
118 lineno = node.lineno
119 for n in node.getChildNodes():
120 lineno = max(lineno, self.getLastLine(n))
121 return lineno
122
123 def doStatement(self, node):
124 self.recordLine(self.getFirstLine(node))
125
126 visitAssert = visitAssign = visitAssTuple = visitDiscard = visitPrint = \
127 visitPrintnl = visitRaise = visitSubscript = visitDecorators = \
128 doStatement
129
130 def recordNodeLine(self, node):
131 return self.recordLine(node.lineno)
132
133 def recordLine(self, lineno):
134 # Returns a bool, whether the line is included or excluded.
135 if lineno:
136 # Multi-line tests introducing suites have to get charged to their
137 # keyword.
138 if lineno in self.suite_spots:
139 lineno = self.suite_spots[lineno][0]
140 # If we're inside an exluded suite, record that this line was
141 # excluded.
142 if self.excluding_suite:
143 self.excluded[lineno] = 1
144 return 0
145 # If this line is excluded, or suite_spots maps this line to
146 # another line that is exlcuded, then we're excluded.
147 elif self.excluded.has_key(lineno) or \
148 self.suite_spots.has_key(lineno) and \
149 self.excluded.has_key(self.suite_spots[lineno][1]):
150 return 0
151 # Otherwise, this is an executable line.
152 else:
153 self.statements[lineno] = 1
154 return 1
155 return 0
156
157 default = recordNodeLine
158
159 def recordAndDispatch(self, node):
160 self.recordNodeLine(node)
161 self.dispatch(node)
162
163 def doSuite(self, intro, body, exclude=0):
164 exsuite = self.excluding_suite
165 if exclude or (intro and not self.recordNodeLine(intro)):
166 self.excluding_suite = 1
167 self.recordAndDispatch(body)
168 self.excluding_suite = exsuite
169
170 def doPlainWordSuite(self, prevsuite, suite):
171 # Finding the exclude lines for else's is tricky, because they aren't
172 # present in the compiler parse tree. Look at the previous suite,
173 # and find its last line. If any line between there and the else's
174 # first line are excluded, then we exclude the else.
175 lastprev = self.getLastLine(prevsuite)
176 firstelse = self.getFirstLine(suite)
177 for l in range(lastprev+1, firstelse):
178 if self.suite_spots.has_key(l):
179 self.doSuite(None, suite, exclude=self.excluded.has_key(l))
180 break
181 else:
182 self.doSuite(None, suite)
183
184 def doElse(self, prevsuite, node):
185 if node.else_:
186 self.doPlainWordSuite(prevsuite, node.else_)
187
188 def visitFor(self, node):
189 self.doSuite(node, node.body)
190 self.doElse(node.body, node)
191
192 def visitIf(self, node):
193 # The first test has to be handled separately from the rest.
194 # The first test is credited to the line with the "if", but the others
195 # are credited to the line with the test for the elif.
196 self.doSuite(node, node.tests[0][1])
197 for t, n in node.tests[1:]:
198 self.doSuite(t, n)
199 self.doElse(node.tests[-1][1], node)
200
201 def visitWhile(self, node):
202 self.doSuite(node, node.body)
203 self.doElse(node.body, node)
204
205 def visitTryExcept(self, node):
206 self.doSuite(node, node.body)
207 for i in range(len(node.handlers)):
208 a, b, h = node.handlers[i]
209 if not a:
210 # It's a plain "except:". Find the previous suite.
211 if i > 0:
212 prev = node.handlers[i-1][2]
213 else:
214 prev = node.body
215 self.doPlainWordSuite(prev, h)
216 else:
217 self.doSuite(a, h)
218 self.doElse(node.handlers[-1][2], node)
219
220 def visitTryFinally(self, node):
221 self.doSuite(node, node.body)
222 self.doPlainWordSuite(node.body, node.final)
223
224 def visitGlobal(self, node):
225 # "global" statements don't execute like others (they don't call the
226 # trace function), so don't record their line numbers.
227 pass
228
229 the_coverage = None
230
231 class coverage:
232 error = "coverage error"
233
234 # Name of the cache file (unless environment variable is set).
235 cache_default = ".coverage"
236
237 # Environment variable naming the cache file.
238 cache_env = "COVERAGE_FILE"
239
240 # A dictionary with an entry for (Python source file name, line number
241 # in that file) if that line has been executed.
242 c = {}
243
244 # A map from canonical Python source file name to a dictionary in
245 # which there's an entry for each line number that has been
246 # executed.
247 cexecuted = {}
248
249 # Cache of results of calling the analysis2() method, so that you can
250 # specify both -r and -a without doing double work.
251 analysis_cache = {}
252
253 # Cache of results of calling the canonical_filename() method, to
254 # avoid duplicating work.
255 canonical_filename_cache = {}
256
257 def __init__(self):
258 global the_coverage
259 if the_coverage:
260 raise self.error, "Only one coverage object allowed."
261 self.usecache = 1
262 self.cache = None
263 self.exclude_re = ''
264 self.nesting = 0
265 self.cstack = []
266 self.xstack = []
267 self.relative_dir = os.path.normcase(os.path.abspath(os.curdir)+os.path.sep)
268
269 # t(f, x, y). This method is passed to sys.settrace as a trace function.
270 # See [van Rossum 2001-07-20b, 9.2] for an explanation of sys.settrace and
271 # the arguments and return value of the trace function.
272 # See [van Rossum 2001-07-20a, 3.2] for a description of frame and code
273 # objects.
274
275 def t(self, f, w, a): #pragma: no cover
276 #print w, f.f_code.co_filename, f.f_lineno
277 if w == 'line':
278 self.c[(f.f_code.co_filename, f.f_lineno)] = 1
279 for c in self.cstack:
280 c[(f.f_code.co_filename, f.f_lineno)] = 1
281 return self.t
282
283 def help(self, error=None):
284 if error:
285 print error
286 print
287 print __doc__
288 sys.exit(1)
289
290 def command_line(self):
291 import getopt
292 settings = {}
293 optmap = {
294 '-a': 'annotate',
295 '-d:': 'directory=',
296 '-e': 'erase',
297 '-h': 'help',
298 '-i': 'ignore-errors',
299 '-m': 'show-missing',
300 '-r': 'report',
301 '-x': 'execute',
302 '-o': 'omit=',
303 }
304 short_opts = string.join(map(lambda o: o[1:], optmap.keys()), '')
305 long_opts = optmap.values()
306 options, args = getopt.getopt(sys.argv[1:], short_opts, long_opts)
307 for o, a in options:
308 if optmap.has_key(o):
309 settings[optmap[o]] = 1
310 elif optmap.has_key(o + ':'):
311 settings[optmap[o + ':']] = a
312 elif o[2:] in long_opts:
313 settings[o[2:]] = 1
314 elif o[2:] + '=' in long_opts:
315 settings[o[2:]] = a
316 else:
317 self.help("Unknown option: '%s'." % o)
318 if settings.get('help'):
319 self.help()
320 for i in ['erase', 'execute']:
321 for j in ['annotate', 'report']:
322 if settings.get(i) and settings.get(j):
323 self.help("You can't specify the '%s' and '%s' "
324 "options at the same time." % (i, j))
325 args_needed = (settings.get('execute')
326 or settings.get('annotate')
327 or settings.get('report'))
328 action = settings.get('erase') or args_needed
329 if not action:
330 self.help("You must specify at least one of -e, -x, -r, or -a.")
331 if not args_needed and args:
332 self.help("Unexpected arguments %s." % args)
333
334 self.get_ready()
335 self.exclude('#pragma[: ]+[nN][oO] [cC][oO][vV][eE][rR]')
336
337 if settings.get('erase'):
338 self.erase()
339 if settings.get('execute'):
340 if not args:
341 self.help("Nothing to do.")
342 sys.argv = args
343 self.start()
344 import __main__
345 sys.path[0] = os.path.dirname(sys.argv[0])
346 execfile(sys.argv[0], __main__.__dict__)
347 if not args:
348 args = self.cexecuted.keys()
349 ignore_errors = settings.get('ignore-errors')
350 show_missing = settings.get('show-missing')
351 directory = settings.get('directory')
352 omit = filter(None, settings.get('omit', '').split(','))
353
354 if settings.get('report'):
355 self.report(args, show_missing, ignore_errors, omit_prefixes=omit)
356 if settings.get('annotate'):
357 self.annotate(args, directory, ignore_errors, omit_prefixes=omit)
358
359 def use_cache(self, usecache):
360 self.usecache = usecache
361
362 def get_ready(self):
363 if self.usecache and not self.cache:
364 self.cache = os.path.abspath(os.environ.get(self.cache_env,
365 self.cache_default))
366 self.restore()
367 self.analysis_cache = {}
368
369 def start(self):
370 self.get_ready()
371 if self.nesting == 0: #pragma: no cover
372 sys.settrace(self.t)
373 if hasattr(threading, 'settrace'):
374 threading.settrace(self.t)
375 self.nesting += 1
376
377 def stop(self):
378 self.nesting -= 1
379 if self.nesting == 0: #pragma: no cover
380 sys.settrace(None)
381 if hasattr(threading, 'settrace'):
382 threading.settrace(None)
383
384 def erase(self):
385 self.c = {}
386 self.analysis_cache = {}
387 self.cexecuted = {}
388 if self.cache and os.path.exists(self.cache):
389 os.remove(self.cache)
390 self.exclude_re = ""
391
392 def exclude(self, re):
393 if self.exclude_re:
394 self.exclude_re += "|"
395 self.exclude_re += "(" + re + ")"
396
397 def begin_recursive(self):
398 self.cstack.append(self.c)
399 self.xstack.append(self.exclude_re)
400
401 def end_recursive(self):
402 self.c = self.cstack.pop()
403 self.exclude_re = self.xstack.pop()
404
405 # save(). Save coverage data to the coverage cache.
406
407 def save(self):
408 # move to directory that must exist.
409 os.chdir(os.sep)
410 if self.usecache and self.cache:
411 self.canonicalize_filenames()
412 cache = open(self.cache, 'wb')
413 import marshal
414 marshal.dump(self.cexecuted, cache)
415 cache.close()
416
417 # restore(). Restore coverage data from the coverage cache (if it exists).
418
419 def restore(self):
420 self.c = {}
421 self.cexecuted = {}
422 assert self.usecache
423 if not os.path.exists(self.cache):
424 return
425 try:
426 cache = open(self.cache, 'rb')
427 import marshal
428 cexecuted = marshal.load(cache)
429 cache.close()
430 if isinstance(cexecuted, types.DictType):
431 self.cexecuted = cexecuted
432 except:
433 pass
434
435 # canonical_filename(filename). Return a canonical filename for the
436 # file (that is, an absolute path with no redundant components and
437 # normalized case). See [GDR 2001-12-04b, 3.3].
438
439 def canonical_filename(self, filename):
440 if not self.canonical_filename_cache.has_key(filename):
441 f = filename
442 if os.path.isabs(f) and not os.path.exists(f):
443 f = os.path.basename(f)
444 if not os.path.isabs(f):
445 for path in [os.curdir] + sys.path:
446 g = os.path.join(path, f)
447 if os.path.exists(g):
448 f = g
449 break
450 cf = os.path.normcase(os.path.abspath(f))
451 self.canonical_filename_cache[filename] = cf
452 return self.canonical_filename_cache[filename]
453
454 # canonicalize_filenames(). Copy results from "c" to "cexecuted",
455 # canonicalizing filenames on the way. Clear the "c" map.
456
457 def canonicalize_filenames(self):
458 for filename, lineno in self.c.keys():
459 f = self.canonical_filename(filename)
460 if not self.cexecuted.has_key(f):
461 self.cexecuted[f] = {}
462 self.cexecuted[f][lineno] = 1
463 self.c = {}
464
465 # morf_filename(morf). Return the filename for a module or file.
466
467 def morf_filename(self, morf):
468 if isinstance(morf, types.ModuleType):
469 if not hasattr(morf, '__file__'):
470 raise self.error, "Module has no __file__ attribute."
471 file = morf.__file__
472 else:
473 file = morf
474 return self.canonical_filename(file)
475
476 # analyze_morf(morf). Analyze the module or filename passed as
477 # the argument. If the source code can't be found, raise an error.
478 # Otherwise, return a tuple of (1) the canonical filename of the
479 # source code for the module, (2) a list of lines of statements
480 # in the source code, and (3) a list of lines of excluded statements.
481
482 def analyze_morf(self, morf):
483 if self.analysis_cache.has_key(morf):
484 return self.analysis_cache[morf]
485 filename = self.morf_filename(morf)
486 ext = os.path.splitext(filename)[1]
487 if ext == '.pyc':
488 if not os.path.exists(filename[0:-1]):
489 raise self.error, ("No source for compiled code '%s'."
490 % filename)
491 filename = filename[0:-1]
492 elif ext != '.py':
493 raise self.error, "File '%s' not Python source." % filename
494 source = open(filename, 'r')
495 lines, excluded_lines = self.find_executable_statements(
496 source.read(), exclude=self.exclude_re
497 )
498 source.close()
499 result = filename, lines, excluded_lines
500 self.analysis_cache[morf] = result
501 return result
502
503 def get_suite_spots(self, tree, spots):
504 import symbol, token
505 for i in range(1, len(tree)):
506 if type(tree[i]) == type(()):
507 if tree[i][0] == symbol.suite:
508 # Found a suite, look back for the colon and keyword.
509 lineno_colon = lineno_word = None
510 for j in range(i-1, 0, -1):
511 if tree[j][0] == token.COLON:
512 lineno_colon = tree[j][2]
513 elif tree[j][0] == token.NAME:
514 if tree[j][1] == 'elif':
515 # Find the line number of the first non-terminal
516 # after the keyword.
517 t = tree[j+1]
518 while t and token.ISNONTERMINAL(t[0]):
519 t = t[1]
520 if t:
521 lineno_word = t[2]
522 else:
523 lineno_word = tree[j][2]
524 break
525 elif tree[j][0] == symbol.except_clause:
526 # "except" clauses look like:
527 # ('except_clause', ('NAME', 'except', lineno), ...)
528 if tree[j][1][0] == token.NAME:
529 lineno_word = tree[j][1][2]
530 break
531 if lineno_colon and lineno_word:
532 # Found colon and keyword, mark all the lines
533 # between the two with the two line numbers.
534 for l in range(lineno_word, lineno_colon+1):
535 spots[l] = (lineno_word, lineno_colon)
536 self.get_suite_spots(tree[i], spots)
537
538 def find_executable_statements(self, text, exclude=None):
539 # Find lines which match an exclusion pattern.
540 excluded = {}
541 suite_spots = {}
542 if exclude:
543 reExclude = re.compile(exclude)
544 lines = text.split('\n')
545 for i in range(len(lines)):
546 if reExclude.search(lines[i]):
547 excluded[i+1] = 1
548
549 import parser
550 tree = parser.suite(text+'\n\n').totuple(1)
551 self.get_suite_spots(tree, suite_spots)
552
553 # Use the compiler module to parse the text and find the executable
554 # statements. We add newlines to be impervious to final partial lines.
555 statements = {}
556 ast = compiler.parse(text+'\n\n')
557 visitor = StatementFindingAstVisitor(statements, excluded, suite_spots)
558 compiler.walk(ast, visitor, walker=visitor)
559
560 lines = statements.keys()
561 lines.sort()
562 excluded_lines = excluded.keys()
563 excluded_lines.sort()
564 return lines, excluded_lines
565
566 # format_lines(statements, lines). Format a list of line numbers
567 # for printing by coalescing groups of lines as long as the lines
568 # represent consecutive statements. This will coalesce even if
569 # there are gaps between statements, so if statements =
570 # [1,2,3,4,5,10,11,12,13,14] and lines = [1,2,5,10,11,13,14] then
571 # format_lines will return "1-2, 5-11, 13-14".
572
573 def format_lines(self, statements, lines):
574 pairs = []
575 i = 0
576 j = 0
577 start = None
578 pairs = []
579 while i < len(statements) and j < len(lines):
580 if statements[i] == lines[j]:
581 if start == None:
582 start = lines[j]
583 end = lines[j]
584 j = j + 1
585 elif start:
586 pairs.append((start, end))
587 start = None
588 i = i + 1
589 if start:
590 pairs.append((start, end))
591 def stringify(pair):
592 start, end = pair
593 if start == end:
594 return "%d" % start
595 else:
596 return "%d-%d" % (start, end)
597 return string.join(map(stringify, pairs), ", ")
598
599 # Backward compatibility with version 1.
600 def analysis(self, morf):
601 f, s, _, m, mf = self.analysis2(morf)
602 return f, s, m, mf
603
604 def analysis2(self, morf):
605 filename, statements, excluded = self.analyze_morf(morf)
606 self.canonicalize_filenames()
607 if not self.cexecuted.has_key(filename):
608 self.cexecuted[filename] = {}
609 missing = []
610 for line in statements:
611 if not self.cexecuted[filename].has_key(line):
612 missing.append(line)
613 return (filename, statements, excluded, missing,
614 self.format_lines(statements, missing))
615
616 def relative_filename(self, filename):
617 """ Convert filename to relative filename from self.relative_dir.
618 """
619 return filename.replace(self.relative_dir, "")
620
621 def morf_name(self, morf):
622 """ Return the name of morf as used in report.
623 """
624 if isinstance(morf, types.ModuleType):
625 return morf.__name__
626 else:
627 return self.relative_filename(os.path.splitext(morf)[0])
628
629 def filter_by_prefix(self, morfs, omit_prefixes):
630 """ Return list of morfs where the morf name does not begin
631 with any one of the omit_prefixes.
632 """
633 filtered_morfs = []
634 for morf in morfs:
635 for prefix in omit_prefixes:
636 if self.morf_name(morf).startswith(prefix):
637 break
638 else:
639 filtered_morfs.append(morf)
640
641 return filtered_morfs
642
643 def morf_name_compare(self, x, y):
644 return cmp(self.morf_name(x), self.morf_name(y))
645
646 def report(self, morfs, show_missing=1, ignore_errors=0, file=None, omit_prefixes=[]):
647 if not isinstance(morfs, types.ListType):
648 morfs = [morfs]
649 morfs = self.filter_by_prefix(morfs, omit_prefixes)
650 morfs.sort(self.morf_name_compare)
651
652 max_name = max([5,] + map(len, map(self.morf_name, morfs)))
653 fmt_name = "%%- %ds " % max_name
654 fmt_err = fmt_name + "%s: %s"
655 header = fmt_name % "Name" + " Stmts Exec Cover"
656 fmt_coverage = fmt_name + "% 6d % 6d % 5d%%"
657 if show_missing:
658 header = header + " Missing"
659 fmt_coverage = fmt_coverage + " %s"
660 if not file:
661 file = sys.stdout
662 print >>file, header
663 print >>file, "-" * len(header)
664 total_statements = 0
665 total_executed = 0
666 for morf in morfs:
667 name = self.morf_name(morf)
668 try:
669 _, statements, _, missing, readable = self.analysis2(morf)
670 n = len(statements)
671 m = n - len(missing)
672 if n > 0:
673 pc = 100.0 * m / n
674 else:
675 pc = 100.0
676 args = (name, n, m, pc)
677 if show_missing:
678 args = args + (readable,)
679 print >>file, fmt_coverage % args
680 total_statements = total_statements + n
681 total_executed = total_executed + m
682 except KeyboardInterrupt: #pragma: no cover
683 raise
684 except:
685 if not ignore_errors:
686 type, msg = sys.exc_info()[0:2]
687 print >>file, fmt_err % (name, type, msg)
688 if len(morfs) > 1:
689 print >>file, "-" * len(header)
690 if total_statements > 0:
691 pc = 100.0 * total_executed / total_statements
692 else:
693 pc = 100.0
694 args = ("TOTAL", total_statements, total_executed, pc)
695 if show_missing:
696 args = args + ("",)
697 print >>file, fmt_coverage % args
698
699 # annotate(morfs, ignore_errors).
700
701 blank_re = re.compile(r"\s*(#|$)")
702 else_re = re.compile(r"\s*else\s*:\s*(#|$)")
703
704 def annotate(self, morfs, directory=None, ignore_errors=0, omit_prefixes=[]):
705 morfs = self.filter_by_prefix(morfs, omit_prefixes)
706 for morf in morfs:
707 try:
708 filename, statements, excluded, missing, _ = self.analysis2(morf)
709 self.annotate_file(filename, statements, excluded, missing, directory)
710 except KeyboardInterrupt:
711 raise
712 except:
713 if not ignore_errors:
714 raise
715
716 def annotate_file(self, filename, statements, excluded, missing, directory=None):
717 source = open(filename, 'r')
718 if directory:
719 dest_file = os.path.join(directory,
720 os.path.basename(filename)
721 + ',cover')
722 else:
723 dest_file = filename + ',cover'
724 dest = open(dest_file, 'w')
725 lineno = 0
726 i = 0
727 j = 0
728 covered = 1
729 while 1:
730 line = source.readline()
731 if line == '':
732 break
733 lineno = lineno + 1
734 while i < len(statements) and statements[i] < lineno:
735 i = i + 1
736 while j < len(missing) and missing[j] < lineno:
737 j = j + 1
738 if i < len(statements) and statements[i] == lineno:
739 covered = j >= len(missing) or missing[j] > lineno
740 if self.blank_re.match(line):
741 dest.write(' ')
742 elif self.else_re.match(line):
743 # Special logic for lines containing only 'else:'.
744 # See [GDR 2001-12-04b, 3.2].
745 if i >= len(statements) and j >= len(missing):
746 dest.write('! ')
747 elif i >= len(statements) or j >= len(missing):
748 dest.write('> ')
749 elif statements[i] == missing[j]:
750 dest.write('! ')
751 else:
752 dest.write('> ')
753 elif lineno in excluded:
754 dest.write('- ')
755 elif covered:
756 dest.write('> ')
757 else:
758 dest.write('! ')
759 dest.write(line)
760 source.close()
761 dest.close()
762
763 # Singleton object.
764 the_coverage = coverage()
765
766 # Module functions call methods in the singleton object.
767 def use_cache(*args, **kw): return the_coverage.use_cache(*args, **kw)
768 def start(*args, **kw): return the_coverage.start(*args, **kw)
769 def stop(*args, **kw): return the_coverage.stop(*args, **kw)
770 def erase(*args, **kw): return the_coverage.erase(*args, **kw)
771 def begin_recursive(*args, **kw): return the_coverage.begin_recursive(*args, **kw)
772 def end_recursive(*args, **kw): return the_coverage.end_recursive(*args, **kw)
773 def exclude(*args, **kw): return the_coverage.exclude(*args, **kw)
774 def analysis(*args, **kw): return the_coverage.analysis(*args, **kw)
775 def analysis2(*args, **kw): return the_coverage.analysis2(*args, **kw)
776 def report(*args, **kw): return the_coverage.report(*args, **kw)
777 def annotate(*args, **kw): return the_coverage.annotate(*args, **kw)
778 def annotate_file(*args, **kw): return the_coverage.annotate_file(*args, **kw)
779
780 # Save coverage data when Python exits. (The atexit module wasn't
781 # introduced until Python 2.0, so use sys.exitfunc when it's not
782 # available.)
783 try:
784 import atexit
785 atexit.register(the_coverage.save)
786 except ImportError:
787 sys.exitfunc = the_coverage.save
788
789 # Command-line interface.
790 if __name__ == '__main__':
791 the_coverage.command_line()
792
793
794 # A. REFERENCES
795 #
796 # [GDR 2001-12-04a] "Statement coverage for Python"; Gareth Rees;
797 # Ravenbrook Limited; 2001-12-04;
798 # <http://www.nedbatchelder.com/code/modules/rees-coverage.html>.
799 #
800 # [GDR 2001-12-04b] "Statement coverage for Python: design and
801 # analysis"; Gareth Rees; Ravenbrook Limited; 2001-12-04;
802 # <http://www.nedbatchelder.com/code/modules/rees-design.html>.
803 #
804 # [van Rossum 2001-07-20a] "Python Reference Manual (releae 2.1.1)";
805 # Guide van Rossum; 2001-07-20;
806 # <http://www.python.org/doc/2.1.1/ref/ref.html>.
807 #
808 # [van Rossum 2001-07-20b] "Python Library Reference"; Guido van Rossum;
809 # 2001-07-20; <http://www.python.org/doc/2.1.1/lib/lib.html>.
810 #
811 #
812 # B. DOCUMENT HISTORY
813 #
814 # 2001-12-04 GDR Created.
815 #
816 # 2001-12-06 GDR Added command-line interface and source code
817 # annotation.
818 #
819 # 2001-12-09 GDR Moved design and interface to separate documents.
820 #
821 # 2001-12-10 GDR Open cache file as binary on Windows. Allow
822 # simultaneous -e and -x, or -a and -r.
823 #
824 # 2001-12-12 GDR Added command-line help. Cache analysis so that it
825 # only needs to be done once when you specify -a and -r.
826 #
827 # 2001-12-13 GDR Improved speed while recording. Portable between
828 # Python 1.5.2 and 2.1.1.
829 #
830 # 2002-01-03 GDR Module-level functions work correctly.
831 #
832 # 2002-01-07 GDR Update sys.path when running a file with the -x option,
833 # so that it matches the value the program would get if it were run on
834 # its own.
835 #
836 # 2004-12-12 NMB Significant code changes.
837 # - Finding executable statements has been rewritten so that docstrings and
838 # other quirks of Python execution aren't mistakenly identified as missing
839 # lines.
840 # - Lines can be excluded from consideration, even entire suites of lines.
841 # - The filesystem cache of covered lines can be disabled programmatically.
842 # - Modernized the code.
843 #
844 # 2004-12-14 NMB Minor tweaks. Return 'analysis' to its original behavior
845 # and add 'analysis2'. Add a global for 'annotate', and factor it, adding
846 # 'annotate_file'.
847 #
848 # 2004-12-31 NMB Allow for keyword arguments in the module global functions.
849 # Thanks, Allen.
850 #
851 # 2005-12-02 NMB Call threading.settrace so that all threads are measured.
852 # Thanks Martin Fuzzey. Add a file argument to report so that reports can be
853 # captured to a different destination.
854 #
855 # 2005-12-03 NMB coverage.py can now measure itself.
856 #
857 # 2005-12-04 NMB Adapted Greg Rogers' patch for using relative filenames,
858 # and sorting and omitting files to report on.
859 #
860 # C. COPYRIGHT AND LICENCE
861 #
862 # Copyright 2001 Gareth Rees. All rights reserved.
863 # Copyright 2004-2005 Ned Batchelder. All rights reserved.
864 #
865 # Redistribution and use in source and binary forms, with or without
866 # modification, are permitted provided that the following conditions are
867 # met:
868 #
869 # 1. Redistributions of source code must retain the above copyright
870 # notice, this list of conditions and the following disclaimer.
871 #
872 # 2. Redistributions in binary form must reproduce the above copyright
873 # notice, this list of conditions and the following disclaimer in the
874 # documentation and/or other materials provided with the
875 # distribution.
876 #
877 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
878 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
879 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
880 # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
881 # HOLDERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
882 # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
883 # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
884 # OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
885 # ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
886 # TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
887 # USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
888 # DAMAGE.
889 #
890 # $Id: coverage.py 26 2005-12-04 18:42:44Z ned $
@@ -0,0 +1,19 b''
1 #!/bin/sh
2
3 hg init a
4 cd a
5 hg init b
6 echo x > b/x
7 echo '# should print nothing'
8 hg st
9 echo '# should print ? b/x'
10 hg st b/x
11
12 hg add b/x
13
14 echo '# should print A b/x'
15 hg st
16 echo '# should forget b/x'
17 hg forget
18 echo '# should print nothing'
19 hg st b
@@ -0,0 +1,8 b''
1 # should print nothing
2 # should print ? b/x
3 ? b/x
4 # should print A b/x
5 A b/x
6 # should forget b/x
7 forgetting b/x
8 # should print nothing
@@ -8,6 +8,7 b' syntax: glob'
8 *.pyc
8 *.pyc
9 *.swp
9 *.swp
10 *.prof
10 *.prof
11 tests/.coverage*
11 tests/*.err
12 tests/*.err
12 build
13 build
13 dist
14 dist
@@ -2,13 +2,13 b' include hg'
2 recursive-include mercurial *.py
2 recursive-include mercurial *.py
3 include hgweb.cgi hgwebdir.cgi
3 include hgweb.cgi hgwebdir.cgi
4 include hgeditor rewrite-log
4 include hgeditor rewrite-log
5 include tests/README tests/run-tests tests/test-*[a-z0-9] tests/*.out
5 include tests/README tests/run-tests tests/md5sum.py tests/test-*[a-z0-9] tests/*.out
6 prune tests/*.err
6 prune tests/*.err
7 include *.txt
7 include *.txt
8 include templates/map templates/map-*[a-z0-9]
8 include templates/map templates/map-*[a-z0-9]
9 include templates/*.tmpl
9 include templates/*.tmpl
10 include templates/static/*
10 include templates/static/*
11 include doc/README doc/Makefile doc/*.txt doc/*.html doc/*.[0-9]
11 include doc/README doc/Makefile doc/gendoc.py doc/*.txt doc/*.html doc/*.[0-9]
12 recursive-include contrib *
12 recursive-include contrib *
13 include README
13 include README
14 include CONTRIBUTORS
14 include CONTRIBUTORS
@@ -3448,7 +3448,7 b' proc domktag {} {'
3448 return
3448 return
3449 }
3449 }
3450 if {[catch {
3450 if {[catch {
3451 set out [exec hg tag $tag $id]
3451 set out [exec hg tag -r $id $tag]
3452 } err]} {
3452 } err]} {
3453 error_popup "Error creating tag: $err"
3453 error_popup "Error creating tag: $err"
3454 return
3454 return
@@ -2259,7 +2259,7 b' def recover(ui, repo):'
2259 """
2259 """
2260 if repo.recover():
2260 if repo.recover():
2261 return repo.verify()
2261 return repo.verify()
2262 return False
2262 return 1
2263
2263
2264 def remove(ui, repo, pat, *pats, **opts):
2264 def remove(ui, repo, pat, *pats, **opts):
2265 """remove the specified files on the next commit
2265 """remove the specified files on the next commit
@@ -3259,38 +3259,32 b' def dispatch(args):'
3259 u = ui.ui()
3259 u = ui.ui()
3260 except util.Abort, inst:
3260 except util.Abort, inst:
3261 sys.stderr.write(_("abort: %s\n") % inst)
3261 sys.stderr.write(_("abort: %s\n") % inst)
3262 sys.exit(1)
3262 return -1
3263
3263
3264 external = []
3264 external = []
3265 for x in u.extensions():
3265 for x in u.extensions():
3266 def on_exception(exc, inst):
3266 try:
3267 u.warn(_("*** failed to import extension %s\n") % x[1])
3267 if x[1]:
3268 u.warn("%s\n" % inst)
3269 if "--traceback" in sys.argv[1:]:
3270 traceback.print_exc()
3271 if x[1]:
3272 try:
3273 mod = imp.load_source(x[0], x[1])
3268 mod = imp.load_source(x[0], x[1])
3274 except Exception, inst:
3269 else:
3275 on_exception(Exception, inst)
3270 def importh(name):
3276 continue
3271 mod = __import__(name)
3277 else:
3272 components = name.split('.')
3278 def importh(name):
3273 for comp in components[1:]:
3279 mod = __import__(name)
3274 mod = getattr(mod, comp)
3280 components = name.split('.')
3275 return mod
3281 for comp in components[1:]:
3282 mod = getattr(mod, comp)
3283 return mod
3284 try:
3285 try:
3276 try:
3286 mod = importh("hgext." + x[0])
3277 mod = importh("hgext." + x[0])
3287 except ImportError:
3278 except ImportError:
3288 mod = importh(x[0])
3279 mod = importh(x[0])
3289 except Exception, inst:
3280 external.append(mod)
3290 on_exception(Exception, inst)
3281 except Exception, inst:
3291 continue
3282 u.warn(_("*** failed to import extension %s: %s\n") % (x[0], inst))
3292
3283 if "--traceback" in sys.argv[1:]:
3293 external.append(mod)
3284 traceback.print_exc()
3285 return 1
3286 continue
3287
3294 for x in external:
3288 for x in external:
3295 cmdtable = getattr(x, 'cmdtable', {})
3289 cmdtable = getattr(x, 'cmdtable', {})
3296 for t in cmdtable:
3290 for t in cmdtable:
@@ -3332,14 +3326,11 b' def dispatch(args):'
3332 repo = path and hg.repository(u, path=path) or None
3326 repo = path and hg.repository(u, path=path) or None
3333
3327
3334 if options['help']:
3328 if options['help']:
3335 help_(u, cmd, options['version'])
3329 return help_(u, cmd, options['version'])
3336 sys.exit(0)
3337 elif options['version']:
3330 elif options['version']:
3338 show_version(u)
3331 return show_version(u)
3339 sys.exit(0)
3340 elif not cmd:
3332 elif not cmd:
3341 help_(u, 'shortlist')
3333 return help_(u, 'shortlist')
3342 sys.exit(0)
3343
3334
3344 if cmd not in norepo.split():
3335 if cmd not in norepo.split():
3345 try:
3336 try:
@@ -3394,15 +3385,12 b' def dispatch(args):'
3394 else:
3385 else:
3395 u.warn(_("hg: %s\n") % inst.args[1])
3386 u.warn(_("hg: %s\n") % inst.args[1])
3396 help_(u, 'shortlist')
3387 help_(u, 'shortlist')
3397 sys.exit(-1)
3398 except AmbiguousCommand, inst:
3388 except AmbiguousCommand, inst:
3399 u.warn(_("hg: command '%s' is ambiguous:\n %s\n") %
3389 u.warn(_("hg: command '%s' is ambiguous:\n %s\n") %
3400 (inst.args[0], " ".join(inst.args[1])))
3390 (inst.args[0], " ".join(inst.args[1])))
3401 sys.exit(1)
3402 except UnknownCommand, inst:
3391 except UnknownCommand, inst:
3403 u.warn(_("hg: unknown command '%s'\n") % inst.args[0])
3392 u.warn(_("hg: unknown command '%s'\n") % inst.args[0])
3404 help_(u, 'shortlist')
3393 help_(u, 'shortlist')
3405 sys.exit(1)
3406 except hg.RepoError, inst:
3394 except hg.RepoError, inst:
3407 u.warn(_("abort: "), inst, "!\n")
3395 u.warn(_("abort: "), inst, "!\n")
3408 except lock.LockHeld, inst:
3396 except lock.LockHeld, inst:
@@ -3449,7 +3437,6 b' def dispatch(args):'
3449 u.warn(_("abort: %s\n") % inst.strerror)
3437 u.warn(_("abort: %s\n") % inst.strerror)
3450 except util.Abort, inst:
3438 except util.Abort, inst:
3451 u.warn(_('abort: '), inst.args[0] % inst.args[1:], '\n')
3439 u.warn(_('abort: '), inst.args[0] % inst.args[1:], '\n')
3452 sys.exit(1)
3453 except TypeError, inst:
3440 except TypeError, inst:
3454 # was this an argument error?
3441 # was this an argument error?
3455 tb = traceback.extract_tb(sys.exc_info()[2])
3442 tb = traceback.extract_tb(sys.exc_info()[2])
@@ -3458,9 +3445,10 b' def dispatch(args):'
3458 u.debug(inst, "\n")
3445 u.debug(inst, "\n")
3459 u.warn(_("%s: invalid arguments\n") % cmd)
3446 u.warn(_("%s: invalid arguments\n") % cmd)
3460 help_(u, cmd)
3447 help_(u, cmd)
3461 except SystemExit:
3448 except SystemExit, inst:
3462 # don't catch this in the catch-all below
3449 # Commands shouldn't sys.exit directly, but give a return code.
3463 raise
3450 # Just in case catch this and and pass exit code to caller.
3451 return inst.code
3464 except:
3452 except:
3465 u.warn(_("** unknown exception encountered, details follow\n"))
3453 u.warn(_("** unknown exception encountered, details follow\n"))
3466 u.warn(_("** report bug details to mercurial@selenic.com\n"))
3454 u.warn(_("** report bug details to mercurial@selenic.com\n"))
@@ -3468,4 +3456,4 b' def dispatch(args):'
3468 % version.get_version())
3456 % version.get_version())
3469 raise
3457 raise
3470
3458
3471 sys.exit(-1)
3459 return -1
@@ -342,7 +342,16 b' class dirstate(object):'
342 names.sort()
342 names.sort()
343 # nd is the top of the repository dir tree
343 # nd is the top of the repository dir tree
344 nd = util.normpath(top[len(self.root) + 1:])
344 nd = util.normpath(top[len(self.root) + 1:])
345 if nd == '.': nd = ''
345 if nd == '.':
346 nd = ''
347 else:
348 # do not recurse into a repo contained in this
349 # one. use bisect to find .hg directory so speed
350 # is good on big directory.
351 hg = bisect.bisect_left(names, '.hg')
352 if hg < len(names) and names[hg] == '.hg':
353 if os.path.isdir(os.path.join(top, '.hg')):
354 continue
346 for f in names:
355 for f in names:
347 np = util.pconvert(os.path.join(nd, f))
356 np = util.pconvert(os.path.join(nd, f))
348 if seen(np):
357 if seen(np):
@@ -1594,8 +1594,9 b' class localrepository(object):'
1594 self.ui.debug(_(" remote %s is newer, get\n") % f)
1594 self.ui.debug(_(" remote %s is newer, get\n") % f)
1595 get[f] = m2[f]
1595 get[f] = m2[f]
1596 s = 1
1596 s = 1
1597 elif f in umap:
1597 elif f in umap or f in added:
1598 # this unknown file is the same as the checkout
1598 # this unknown file is the same as the checkout
1599 # we need to reset the dirstate if the file was added
1599 get[f] = m2[f]
1600 get[f] = m2[f]
1600
1601
1601 if not s and mfw[f] != mf2[f]:
1602 if not s and mfw[f] != mf2[f]:
@@ -71,10 +71,23 b' def filter(s, cmd):'
71 return fn(s, cmd[len(name):].lstrip())
71 return fn(s, cmd[len(name):].lstrip())
72 return pipefilter(s, cmd)
72 return pipefilter(s, cmd)
73
73
74 def find_in_path(name, path, default=None):
75 '''find name in search path. path can be string (will be split
76 with os.pathsep), or iterable thing that returns strings. if name
77 found, return path to name. else return default.'''
78 if isinstance(path, str):
79 path = path.split(os.pathsep)
80 for p in path:
81 p_name = os.path.join(p, name)
82 if os.path.exists(p_name):
83 return p_name
84 return default
85
74 def patch(strip, patchname, ui):
86 def patch(strip, patchname, ui):
75 """apply the patch <patchname> to the working directory.
87 """apply the patch <patchname> to the working directory.
76 a list of patched files is returned"""
88 a list of patched files is returned"""
77 fp = os.popen('patch -p%d < "%s"' % (strip, patchname))
89 patcher = find_in_path('gpatch', os.environ.get('PATH', ''), 'patch')
90 fp = os.popen('"%s" -p%d < "%s"' % (patcher, strip, patchname))
78 files = {}
91 files = {}
79 for line in fp:
92 for line in fp:
80 line = line.rstrip()
93 line = line.rstrip()
@@ -373,8 +386,10 b' def unlink(f):'
373 """unlink and remove the directory if it is empty"""
386 """unlink and remove the directory if it is empty"""
374 os.unlink(f)
387 os.unlink(f)
375 # try removing directories that might now be empty
388 # try removing directories that might now be empty
376 try: os.removedirs(os.path.dirname(f))
389 try:
377 except: pass
390 os.removedirs(os.path.dirname(f))
391 except OSError:
392 pass
378
393
379 def copyfiles(src, dst, hardlink=None):
394 def copyfiles(src, dst, hardlink=None):
380 """Copy a directory tree using hardlinks if possible"""
395 """Copy a directory tree using hardlinks if possible"""
@@ -530,18 +545,13 b" if os.name == 'nt':"
530
545
531 sys.stdout = winstdout(sys.stdout)
546 sys.stdout = winstdout(sys.stdout)
532
547
548 def system_rcpath():
549 return [r'c:\mercurial\mercurial.ini']
550
533 def os_rcpath():
551 def os_rcpath():
534 '''return default os-specific hgrc search path'''
552 '''return default os-specific hgrc search path'''
535 try:
553 return system_rcpath() + [os.path.join(os.path.expanduser('~'),
536 import win32api, win32process
554 'mercurial.ini')]
537 proc = win32api.GetCurrentProcess()
538 filename = win32process.GetModuleFileNameEx(proc, 0)
539 systemrc = os.path.join(os.path.dirname(filename), 'mercurial.ini')
540 except ImportError:
541 systemrc = r'c:\mercurial\mercurial.ini'
542
543 return [systemrc,
544 os.path.join(os.path.expanduser('~'), 'mercurial.ini')]
545
555
546 def parse_patch_output(output_line):
556 def parse_patch_output(output_line):
547 """parses the output produced by patch and returns the file name"""
557 """parses the output produced by patch and returns the file name"""
@@ -550,43 +560,9 b" if os.name == 'nt':"
550 pf = pf[1:-1] # Remove the quotes
560 pf = pf[1:-1] # Remove the quotes
551 return pf
561 return pf
552
562
553 try: # Mark Hammond's win32all package allows better functionality on Windows
563 def testpid(pid):
554 import win32api, win32con, win32file, pywintypes
564 '''return False if pid dead, True if running or not known'''
555
565 return True
556 # create hard links using win32file module
557 def os_link(src, dst): # NB will only succeed on NTFS
558 win32file.CreateHardLink(dst, src)
559
560 def nlinks(pathname):
561 """Return number of hardlinks for the given file."""
562 try:
563 fh = win32file.CreateFile(pathname,
564 win32file.GENERIC_READ, win32file.FILE_SHARE_READ,
565 None, win32file.OPEN_EXISTING, 0, None)
566 res = win32file.GetFileInformationByHandle(fh)
567 fh.Close()
568 return res[7]
569 except:
570 return os.stat(pathname).st_nlink
571
572 def testpid(pid):
573 '''return True if pid is still running or unable to
574 determine, False otherwise'''
575 try:
576 import win32process, winerror
577 handle = win32api.OpenProcess(
578 win32con.PROCESS_QUERY_INFORMATION, False, pid)
579 if handle:
580 status = win32process.GetExitCodeProcess(handle)
581 return status == win32con.STILL_ACTIVE
582 except pywintypes.error, details:
583 return details[0] != winerror.ERROR_INVALID_PARAMETER
584 return True
585
586 except ImportError:
587 def testpid(pid):
588 '''return False if pid dead, True if running or not known'''
589 return True
590
566
591 def is_exec(f, last):
567 def is_exec(f, last):
592 return last
568 return last
@@ -612,6 +588,12 b" if os.name == 'nt':"
612 def explain_exit(code):
588 def explain_exit(code):
613 return _("exited with status %d") % code, code
589 return _("exited with status %d") % code, code
614
590
591 try:
592 # override functions with win32 versions if possible
593 from util_win32 import *
594 except ImportError:
595 pass
596
615 else:
597 else:
616 nulldev = '/dev/null'
598 nulldev = '/dev/null'
617
599
@@ -1,4 +1,9 b''
1 #!/bin/sh -e
1 #!/bin/sh -e
2 #
3 # environment variables:
4 #
5 # TEST_COVERAGE - set non-empty if you want to print test coverage report
6 # COVERAGE_STDLIB - set non-empty to report coverage of standard library
2
7
3 LANG="C"; export LANG
8 LANG="C"; export LANG
4 LC_CTYPE="C"; export LC_CTYPE
9 LC_CTYPE="C"; export LC_CTYPE
@@ -19,6 +24,16 b' HGEDITOR=true; export HGEDITOR'
19 HGMERGE=true; export HGMERGE
24 HGMERGE=true; export HGMERGE
20 HGUSER="test"; export HGUSER
25 HGUSER="test"; export HGUSER
21 HGRCPATH=""; export HGRCPATH
26 HGRCPATH=""; export HGRCPATH
27 OS=`uname`
28
29 case "$OS" in
30 HP-UX|SunOS)
31 DIFFOPTS=
32 ;;
33 *)
34 DIFFOPTS=-u
35 ;;
36 esac
22
37
23 if [ `echo -n HG` = "-n HG" ]
38 if [ `echo -n HG` = "-n HG" ]
24 then
39 then
@@ -64,7 +79,19 b' else'
64 fi
79 fi
65 cd "$TESTDIR"
80 cd "$TESTDIR"
66
81
67 BINDIR="$INST/bin"
82 BINDIR="$INST/bin"; export BINDIR
83 if [ -n "$TEST_COVERAGE" ]; then
84 COVERAGE_FILE="$TESTDIR/.coverage"; export COVERAGE_FILE
85 rm -f "$COVERAGE_FILE"
86 mv "$BINDIR/hg" "$BINDIR/hg.py"
87 {
88 echo '#!/bin/sh'
89 echo "exec \"${PYTHON-python}\" \"$TESTDIR/coverage.py\"" \
90 "-x \"$BINDIR/hg.py\" \"\$@\""
91 } > "$BINDIR/hg"
92 chmod 700 "$BINDIR/hg"
93 fi
94
68 PATH="$BINDIR:$PATH"; export PATH
95 PATH="$BINDIR:$PATH"; export PATH
69 if [ -n "$PYTHON" ]; then
96 if [ -n "$PYTHON" ]; then
70 {
97 {
@@ -101,13 +128,13 b' run_one() {'
101 cat "$ERR"
128 cat "$ERR"
102 fail=1
129 fail=1
103 elif [ -r "$OUTOK" ]; then
130 elif [ -r "$OUTOK" ]; then
104 if diff -u "$OUTOK" "$OUT" > /dev/null; then
131 if diff $DIFFOPTS "$OUTOK" "$OUT" > /dev/null; then
105 : no differences
132 : no differences
106 else
133 else
107 cp "$OUT" "$ERR"
134 cp "$OUT" "$ERR"
108 echo
135 echo
109 echo "$1 output changed:"
136 echo "$1 output changed:"
110 diff -u "$OUTOK" "$ERR" || true
137 diff $DIFFOPTS "$OUTOK" "$ERR" || true
111 fail=1
138 fail=1
112 fi
139 fi
113 fi
140 fi
@@ -153,6 +180,17 b' done'
153 echo
180 echo
154 echo "Ran $tests tests, $failed failed."
181 echo "Ran $tests tests, $failed failed."
155
182
183 if [ -n "$TEST_COVERAGE" ]; then
184 unset PYTHONPATH
185 $ECHO_N "$BINDIR,$TESTDIR,$HGTMP/test-," > "$HGTMP/omit"
186 if [ -z "$COVERAGE_STDLIB" ]; then
187 "${PYTHON-python}" -c 'import sys; print ",".join(sys.path)' \
188 >> "$HGTMP/omit"
189 fi
190 cd "$PYTHONDIR"
191 "${PYTHON-python}" "$TESTDIR/coverage.py" -r --omit="`cat \"$HGTMP/omit\"`"
192 fi
193
156 if [ $failed -gt 0 ] ; then
194 if [ $failed -gt 0 ] ; then
157 exit 1
195 exit 1
158 fi
196 fi
@@ -6,7 +6,7 b' 255'
6 abort: repository a not found!
6 abort: repository a not found!
7 255
7 255
8 abort: destination '../a' already exists
8 abort: destination '../a' already exists
9 1
9 255
10 abort: repository a not found!
10 abort: repository a not found!
11 255
11 255
12 abort: destination 'q' already exists
12 abort: destination 'q' already exists
@@ -55,3 +55,15 b' hg --debug up -f -m'
55 hg parents
55 hg parents
56 hg diff | sed -e "s/\(+++ [a-zA-Z0-9_/.-]*\).*/\1/" \
56 hg diff | sed -e "s/\(+++ [a-zA-Z0-9_/.-]*\).*/\1/" \
57 -e "s/\(--- [a-zA-Z0-9_/.-]*\).*/\1/"
57 -e "s/\(--- [a-zA-Z0-9_/.-]*\).*/\1/"
58
59 # test a local add
60 cd ..
61 hg init a
62 hg init b
63 echo a > a/a
64 echo a > b/a
65 hg --cwd a commit -A -m a
66 cd b
67 hg add a
68 hg pull -u ../a
69 hg st
@@ -136,3 +136,10 b' diff -r 802f095af299 a'
136 @@ -1,1 +1,1 @@ a2
136 @@ -1,1 +1,1 @@ a2
137 -a2
137 -a2
138 +abc
138 +abc
139 adding a
140 pulling from ../a
141 requesting all changes
142 adding changesets
143 adding manifests
144 adding file changes
145 added 1 changesets with 1 changes to 1 files
General Comments 0
You need to be logged in to leave comments. Login now