##// END OF EJS Templates
py3: replace str(None) with literal in convcmd.py
Yuya Nishihara -
r34355:ee10eb66 default
parent child Browse files
Show More
@@ -1,611 +1,611 b''
1 1 # convcmd - convert extension commands definition
2 2 #
3 3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 4 #
5 5 # This software may be used and distributed according to the terms of the
6 6 # GNU General Public License version 2 or any later version.
7 7 from __future__ import absolute_import
8 8
9 9 import os
10 10 import shlex
11 11 import shutil
12 12
13 13 from mercurial.i18n import _
14 14 from mercurial import (
15 15 encoding,
16 16 error,
17 17 hg,
18 18 util,
19 19 )
20 20
21 21 from . import (
22 22 bzr,
23 23 common,
24 24 cvs,
25 25 darcs,
26 26 filemap,
27 27 git,
28 28 gnuarch,
29 29 hg as hgconvert,
30 30 monotone,
31 31 p4,
32 32 subversion,
33 33 )
34 34
35 35 mapfile = common.mapfile
36 36 MissingTool = common.MissingTool
37 37 NoRepo = common.NoRepo
38 38 SKIPREV = common.SKIPREV
39 39
40 40 bzr_source = bzr.bzr_source
41 41 convert_cvs = cvs.convert_cvs
42 42 convert_git = git.convert_git
43 43 darcs_source = darcs.darcs_source
44 44 gnuarch_source = gnuarch.gnuarch_source
45 45 mercurial_sink = hgconvert.mercurial_sink
46 46 mercurial_source = hgconvert.mercurial_source
47 47 monotone_source = monotone.monotone_source
48 48 p4_source = p4.p4_source
49 49 svn_sink = subversion.svn_sink
50 50 svn_source = subversion.svn_source
51 51
52 52 orig_encoding = 'ascii'
53 53
54 54 def recode(s):
55 55 if isinstance(s, unicode):
56 56 return s.encode(orig_encoding, 'replace')
57 57 else:
58 58 return s.decode('utf-8').encode(orig_encoding, 'replace')
59 59
60 60 def mapbranch(branch, branchmap):
61 61 '''
62 62 >>> bmap = {b'default': b'branch1'}
63 63 >>> for i in [b'', None]:
64 64 ... mapbranch(i, bmap)
65 65 'branch1'
66 66 'branch1'
67 67 >>> bmap = {b'None': b'branch2'}
68 68 >>> for i in [b'', None]:
69 69 ... mapbranch(i, bmap)
70 70 'branch2'
71 71 'branch2'
72 72 >>> bmap = {b'None': b'branch3', b'default': b'branch4'}
73 73 >>> for i in [b'None', b'', None, b'default', b'branch5']:
74 74 ... mapbranch(i, bmap)
75 75 'branch3'
76 76 'branch4'
77 77 'branch4'
78 78 'branch4'
79 79 'branch5'
80 80 '''
81 81 # If branch is None or empty, this commit is coming from the source
82 82 # repository's default branch and destined for the default branch in the
83 83 # destination repository. For such commits, using a literal "default"
84 84 # in branchmap below allows the user to map "default" to an alternate
85 85 # default branch in the destination repository.
86 86 branch = branchmap.get(branch or 'default', branch)
87 87 # At some point we used "None" literal to denote the default branch,
88 88 # attempt to use that for backward compatibility.
89 89 if (not branch):
90 branch = branchmap.get(str(None), branch)
90 branch = branchmap.get('None', branch)
91 91 return branch
92 92
93 93 source_converters = [
94 94 ('cvs', convert_cvs, 'branchsort'),
95 95 ('git', convert_git, 'branchsort'),
96 96 ('svn', svn_source, 'branchsort'),
97 97 ('hg', mercurial_source, 'sourcesort'),
98 98 ('darcs', darcs_source, 'branchsort'),
99 99 ('mtn', monotone_source, 'branchsort'),
100 100 ('gnuarch', gnuarch_source, 'branchsort'),
101 101 ('bzr', bzr_source, 'branchsort'),
102 102 ('p4', p4_source, 'branchsort'),
103 103 ]
104 104
105 105 sink_converters = [
106 106 ('hg', mercurial_sink),
107 107 ('svn', svn_sink),
108 108 ]
109 109
110 110 def convertsource(ui, path, type, revs):
111 111 exceptions = []
112 112 if type and type not in [s[0] for s in source_converters]:
113 113 raise error.Abort(_('%s: invalid source repository type') % type)
114 114 for name, source, sortmode in source_converters:
115 115 try:
116 116 if not type or name == type:
117 117 return source(ui, path, revs), sortmode
118 118 except (NoRepo, MissingTool) as inst:
119 119 exceptions.append(inst)
120 120 if not ui.quiet:
121 121 for inst in exceptions:
122 122 ui.write("%s\n" % inst)
123 123 raise error.Abort(_('%s: missing or unsupported repository') % path)
124 124
125 125 def convertsink(ui, path, type):
126 126 if type and type not in [s[0] for s in sink_converters]:
127 127 raise error.Abort(_('%s: invalid destination repository type') % type)
128 128 for name, sink in sink_converters:
129 129 try:
130 130 if not type or name == type:
131 131 return sink(ui, path)
132 132 except NoRepo as inst:
133 133 ui.note(_("convert: %s\n") % inst)
134 134 except MissingTool as inst:
135 135 raise error.Abort('%s\n' % inst)
136 136 raise error.Abort(_('%s: unknown repository type') % path)
137 137
138 138 class progresssource(object):
139 139 def __init__(self, ui, source, filecount):
140 140 self.ui = ui
141 141 self.source = source
142 142 self.filecount = filecount
143 143 self.retrieved = 0
144 144
145 145 def getfile(self, file, rev):
146 146 self.retrieved += 1
147 147 self.ui.progress(_('getting files'), self.retrieved,
148 148 item=file, total=self.filecount, unit=_('files'))
149 149 return self.source.getfile(file, rev)
150 150
151 151 def targetfilebelongstosource(self, targetfilename):
152 152 return self.source.targetfilebelongstosource(targetfilename)
153 153
154 154 def lookuprev(self, rev):
155 155 return self.source.lookuprev(rev)
156 156
157 157 def close(self):
158 158 self.ui.progress(_('getting files'), None)
159 159
160 160 class converter(object):
161 161 def __init__(self, ui, source, dest, revmapfile, opts):
162 162
163 163 self.source = source
164 164 self.dest = dest
165 165 self.ui = ui
166 166 self.opts = opts
167 167 self.commitcache = {}
168 168 self.authors = {}
169 169 self.authorfile = None
170 170
171 171 # Record converted revisions persistently: maps source revision
172 172 # ID to target revision ID (both strings). (This is how
173 173 # incremental conversions work.)
174 174 self.map = mapfile(ui, revmapfile)
175 175
176 176 # Read first the dst author map if any
177 177 authorfile = self.dest.authorfile()
178 178 if authorfile and os.path.exists(authorfile):
179 179 self.readauthormap(authorfile)
180 180 # Extend/Override with new author map if necessary
181 181 if opts.get('authormap'):
182 182 self.readauthormap(opts.get('authormap'))
183 183 self.authorfile = self.dest.authorfile()
184 184
185 185 self.splicemap = self.parsesplicemap(opts.get('splicemap'))
186 186 self.branchmap = mapfile(ui, opts.get('branchmap'))
187 187
188 188 def parsesplicemap(self, path):
189 189 """ check and validate the splicemap format and
190 190 return a child/parents dictionary.
191 191 Format checking has two parts.
192 192 1. generic format which is same across all source types
193 193 2. specific format checking which may be different for
194 194 different source type. This logic is implemented in
195 195 checkrevformat function in source files like
196 196 hg.py, subversion.py etc.
197 197 """
198 198
199 199 if not path:
200 200 return {}
201 201 m = {}
202 202 try:
203 203 fp = open(path, 'r')
204 204 for i, line in enumerate(util.iterfile(fp)):
205 205 line = line.splitlines()[0].rstrip()
206 206 if not line:
207 207 # Ignore blank lines
208 208 continue
209 209 # split line
210 210 lex = shlex.shlex(line, posix=True)
211 211 lex.whitespace_split = True
212 212 lex.whitespace += ','
213 213 line = list(lex)
214 214 # check number of parents
215 215 if not (2 <= len(line) <= 3):
216 216 raise error.Abort(_('syntax error in %s(%d): child parent1'
217 217 '[,parent2] expected') % (path, i + 1))
218 218 for part in line:
219 219 self.source.checkrevformat(part)
220 220 child, p1, p2 = line[0], line[1:2], line[2:]
221 221 if p1 == p2:
222 222 m[child] = p1
223 223 else:
224 224 m[child] = p1 + p2
225 225 # if file does not exist or error reading, exit
226 226 except IOError:
227 227 raise error.Abort(_('splicemap file not found or error reading %s:')
228 228 % path)
229 229 return m
230 230
231 231
232 232 def walktree(self, heads):
233 233 '''Return a mapping that identifies the uncommitted parents of every
234 234 uncommitted changeset.'''
235 235 visit = heads
236 236 known = set()
237 237 parents = {}
238 238 numcommits = self.source.numcommits()
239 239 while visit:
240 240 n = visit.pop(0)
241 241 if n in known:
242 242 continue
243 243 if n in self.map:
244 244 m = self.map[n]
245 245 if m == SKIPREV or self.dest.hascommitfrommap(m):
246 246 continue
247 247 known.add(n)
248 248 self.ui.progress(_('scanning'), len(known), unit=_('revisions'),
249 249 total=numcommits)
250 250 commit = self.cachecommit(n)
251 251 parents[n] = []
252 252 for p in commit.parents:
253 253 parents[n].append(p)
254 254 visit.append(p)
255 255 self.ui.progress(_('scanning'), None)
256 256
257 257 return parents
258 258
259 259 def mergesplicemap(self, parents, splicemap):
260 260 """A splicemap redefines child/parent relationships. Check the
261 261 map contains valid revision identifiers and merge the new
262 262 links in the source graph.
263 263 """
264 264 for c in sorted(splicemap):
265 265 if c not in parents:
266 266 if not self.dest.hascommitforsplicemap(self.map.get(c, c)):
267 267 # Could be in source but not converted during this run
268 268 self.ui.warn(_('splice map revision %s is not being '
269 269 'converted, ignoring\n') % c)
270 270 continue
271 271 pc = []
272 272 for p in splicemap[c]:
273 273 # We do not have to wait for nodes already in dest.
274 274 if self.dest.hascommitforsplicemap(self.map.get(p, p)):
275 275 continue
276 276 # Parent is not in dest and not being converted, not good
277 277 if p not in parents:
278 278 raise error.Abort(_('unknown splice map parent: %s') % p)
279 279 pc.append(p)
280 280 parents[c] = pc
281 281
282 282 def toposort(self, parents, sortmode):
283 283 '''Return an ordering such that every uncommitted changeset is
284 284 preceded by all its uncommitted ancestors.'''
285 285
286 286 def mapchildren(parents):
287 287 """Return a (children, roots) tuple where 'children' maps parent
288 288 revision identifiers to children ones, and 'roots' is the list of
289 289 revisions without parents. 'parents' must be a mapping of revision
290 290 identifier to its parents ones.
291 291 """
292 292 visit = sorted(parents)
293 293 seen = set()
294 294 children = {}
295 295 roots = []
296 296
297 297 while visit:
298 298 n = visit.pop(0)
299 299 if n in seen:
300 300 continue
301 301 seen.add(n)
302 302 # Ensure that nodes without parents are present in the
303 303 # 'children' mapping.
304 304 children.setdefault(n, [])
305 305 hasparent = False
306 306 for p in parents[n]:
307 307 if p not in self.map:
308 308 visit.append(p)
309 309 hasparent = True
310 310 children.setdefault(p, []).append(n)
311 311 if not hasparent:
312 312 roots.append(n)
313 313
314 314 return children, roots
315 315
316 316 # Sort functions are supposed to take a list of revisions which
317 317 # can be converted immediately and pick one
318 318
319 319 def makebranchsorter():
320 320 """If the previously converted revision has a child in the
321 321 eligible revisions list, pick it. Return the list head
322 322 otherwise. Branch sort attempts to minimize branch
323 323 switching, which is harmful for Mercurial backend
324 324 compression.
325 325 """
326 326 prev = [None]
327 327 def picknext(nodes):
328 328 next = nodes[0]
329 329 for n in nodes:
330 330 if prev[0] in parents[n]:
331 331 next = n
332 332 break
333 333 prev[0] = next
334 334 return next
335 335 return picknext
336 336
337 337 def makesourcesorter():
338 338 """Source specific sort."""
339 339 keyfn = lambda n: self.commitcache[n].sortkey
340 340 def picknext(nodes):
341 341 return sorted(nodes, key=keyfn)[0]
342 342 return picknext
343 343
344 344 def makeclosesorter():
345 345 """Close order sort."""
346 346 keyfn = lambda n: ('close' not in self.commitcache[n].extra,
347 347 self.commitcache[n].sortkey)
348 348 def picknext(nodes):
349 349 return sorted(nodes, key=keyfn)[0]
350 350 return picknext
351 351
352 352 def makedatesorter():
353 353 """Sort revisions by date."""
354 354 dates = {}
355 355 def getdate(n):
356 356 if n not in dates:
357 357 dates[n] = util.parsedate(self.commitcache[n].date)
358 358 return dates[n]
359 359
360 360 def picknext(nodes):
361 361 return min([(getdate(n), n) for n in nodes])[1]
362 362
363 363 return picknext
364 364
365 365 if sortmode == 'branchsort':
366 366 picknext = makebranchsorter()
367 367 elif sortmode == 'datesort':
368 368 picknext = makedatesorter()
369 369 elif sortmode == 'sourcesort':
370 370 picknext = makesourcesorter()
371 371 elif sortmode == 'closesort':
372 372 picknext = makeclosesorter()
373 373 else:
374 374 raise error.Abort(_('unknown sort mode: %s') % sortmode)
375 375
376 376 children, actives = mapchildren(parents)
377 377
378 378 s = []
379 379 pendings = {}
380 380 while actives:
381 381 n = picknext(actives)
382 382 actives.remove(n)
383 383 s.append(n)
384 384
385 385 # Update dependents list
386 386 for c in children.get(n, []):
387 387 if c not in pendings:
388 388 pendings[c] = [p for p in parents[c] if p not in self.map]
389 389 try:
390 390 pendings[c].remove(n)
391 391 except ValueError:
392 392 raise error.Abort(_('cycle detected between %s and %s')
393 393 % (recode(c), recode(n)))
394 394 if not pendings[c]:
395 395 # Parents are converted, node is eligible
396 396 actives.insert(0, c)
397 397 pendings[c] = None
398 398
399 399 if len(s) != len(parents):
400 400 raise error.Abort(_("not all revisions were sorted"))
401 401
402 402 return s
403 403
404 404 def writeauthormap(self):
405 405 authorfile = self.authorfile
406 406 if authorfile:
407 407 self.ui.status(_('writing author map file %s\n') % authorfile)
408 408 ofile = open(authorfile, 'w+')
409 409 for author in self.authors:
410 410 ofile.write("%s=%s\n" % (author, self.authors[author]))
411 411 ofile.close()
412 412
413 413 def readauthormap(self, authorfile):
414 414 afile = open(authorfile, 'r')
415 415 for line in afile:
416 416
417 417 line = line.strip()
418 418 if not line or line.startswith('#'):
419 419 continue
420 420
421 421 try:
422 422 srcauthor, dstauthor = line.split('=', 1)
423 423 except ValueError:
424 424 msg = _('ignoring bad line in author map file %s: %s\n')
425 425 self.ui.warn(msg % (authorfile, line.rstrip()))
426 426 continue
427 427
428 428 srcauthor = srcauthor.strip()
429 429 dstauthor = dstauthor.strip()
430 430 if self.authors.get(srcauthor) in (None, dstauthor):
431 431 msg = _('mapping author %s to %s\n')
432 432 self.ui.debug(msg % (srcauthor, dstauthor))
433 433 self.authors[srcauthor] = dstauthor
434 434 continue
435 435
436 436 m = _('overriding mapping for author %s, was %s, will be %s\n')
437 437 self.ui.status(m % (srcauthor, self.authors[srcauthor], dstauthor))
438 438
439 439 afile.close()
440 440
441 441 def cachecommit(self, rev):
442 442 commit = self.source.getcommit(rev)
443 443 commit.author = self.authors.get(commit.author, commit.author)
444 444 commit.branch = mapbranch(commit.branch, self.branchmap)
445 445 self.commitcache[rev] = commit
446 446 return commit
447 447
448 448 def copy(self, rev):
449 449 commit = self.commitcache[rev]
450 450 full = self.opts.get('full')
451 451 changes = self.source.getchanges(rev, full)
452 452 if isinstance(changes, basestring):
453 453 if changes == SKIPREV:
454 454 dest = SKIPREV
455 455 else:
456 456 dest = self.map[changes]
457 457 self.map[rev] = dest
458 458 return
459 459 files, copies, cleanp2 = changes
460 460 pbranches = []
461 461 if commit.parents:
462 462 for prev in commit.parents:
463 463 if prev not in self.commitcache:
464 464 self.cachecommit(prev)
465 465 pbranches.append((self.map[prev],
466 466 self.commitcache[prev].branch))
467 467 self.dest.setbranch(commit.branch, pbranches)
468 468 try:
469 469 parents = self.splicemap[rev]
470 470 self.ui.status(_('spliced in %s as parents of %s\n') %
471 471 (_(' and ').join(parents), rev))
472 472 parents = [self.map.get(p, p) for p in parents]
473 473 except KeyError:
474 474 parents = [b[0] for b in pbranches]
475 475 parents.extend(self.map[x]
476 476 for x in commit.optparents
477 477 if x in self.map)
478 478 if len(pbranches) != 2:
479 479 cleanp2 = set()
480 480 if len(parents) < 3:
481 481 source = progresssource(self.ui, self.source, len(files))
482 482 else:
483 483 # For an octopus merge, we end up traversing the list of
484 484 # changed files N-1 times. This tweak to the number of
485 485 # files makes it so the progress bar doesn't overflow
486 486 # itself.
487 487 source = progresssource(self.ui, self.source,
488 488 len(files) * (len(parents) - 1))
489 489 newnode = self.dest.putcommit(files, copies, parents, commit,
490 490 source, self.map, full, cleanp2)
491 491 source.close()
492 492 self.source.converted(rev, newnode)
493 493 self.map[rev] = newnode
494 494
495 495 def convert(self, sortmode):
496 496 try:
497 497 self.source.before()
498 498 self.dest.before()
499 499 self.source.setrevmap(self.map)
500 500 self.ui.status(_("scanning source...\n"))
501 501 heads = self.source.getheads()
502 502 parents = self.walktree(heads)
503 503 self.mergesplicemap(parents, self.splicemap)
504 504 self.ui.status(_("sorting...\n"))
505 505 t = self.toposort(parents, sortmode)
506 506 num = len(t)
507 507 c = None
508 508
509 509 self.ui.status(_("converting...\n"))
510 510 for i, c in enumerate(t):
511 511 num -= 1
512 512 desc = self.commitcache[c].desc
513 513 if "\n" in desc:
514 514 desc = desc.splitlines()[0]
515 515 # convert log message to local encoding without using
516 516 # tolocal() because the encoding.encoding convert()
517 517 # uses is 'utf-8'
518 518 self.ui.status("%d %s\n" % (num, recode(desc)))
519 519 self.ui.note(_("source: %s\n") % recode(c))
520 520 self.ui.progress(_('converting'), i, unit=_('revisions'),
521 521 total=len(t))
522 522 self.copy(c)
523 523 self.ui.progress(_('converting'), None)
524 524
525 525 if not self.ui.configbool('convert', 'skiptags'):
526 526 tags = self.source.gettags()
527 527 ctags = {}
528 528 for k in tags:
529 529 v = tags[k]
530 530 if self.map.get(v, SKIPREV) != SKIPREV:
531 531 ctags[k] = self.map[v]
532 532
533 533 if c and ctags:
534 534 nrev, tagsparent = self.dest.puttags(ctags)
535 535 if nrev and tagsparent:
536 536 # write another hash correspondence to override the
537 537 # previous one so we don't end up with extra tag heads
538 538 tagsparents = [e for e in self.map.iteritems()
539 539 if e[1] == tagsparent]
540 540 if tagsparents:
541 541 self.map[tagsparents[0][0]] = nrev
542 542
543 543 bookmarks = self.source.getbookmarks()
544 544 cbookmarks = {}
545 545 for k in bookmarks:
546 546 v = bookmarks[k]
547 547 if self.map.get(v, SKIPREV) != SKIPREV:
548 548 cbookmarks[k] = self.map[v]
549 549
550 550 if c and cbookmarks:
551 551 self.dest.putbookmarks(cbookmarks)
552 552
553 553 self.writeauthormap()
554 554 finally:
555 555 self.cleanup()
556 556
557 557 def cleanup(self):
558 558 try:
559 559 self.dest.after()
560 560 finally:
561 561 self.source.after()
562 562 self.map.close()
563 563
564 564 def convert(ui, src, dest=None, revmapfile=None, **opts):
565 565 global orig_encoding
566 566 orig_encoding = encoding.encoding
567 567 encoding.encoding = 'UTF-8'
568 568
569 569 # support --authors as an alias for --authormap
570 570 if not opts.get('authormap'):
571 571 opts['authormap'] = opts.get('authors')
572 572
573 573 if not dest:
574 574 dest = hg.defaultdest(src) + "-hg"
575 575 ui.status(_("assuming destination %s\n") % dest)
576 576
577 577 destc = convertsink(ui, dest, opts.get('dest_type'))
578 578
579 579 try:
580 580 srcc, defaultsort = convertsource(ui, src, opts.get('source_type'),
581 581 opts.get('rev'))
582 582 except Exception:
583 583 for path in destc.created:
584 584 shutil.rmtree(path, True)
585 585 raise
586 586
587 587 sortmodes = ('branchsort', 'datesort', 'sourcesort', 'closesort')
588 588 sortmode = [m for m in sortmodes if opts.get(m)]
589 589 if len(sortmode) > 1:
590 590 raise error.Abort(_('more than one sort mode specified'))
591 591 if sortmode:
592 592 sortmode = sortmode[0]
593 593 else:
594 594 sortmode = defaultsort
595 595
596 596 if sortmode == 'sourcesort' and not srcc.hasnativeorder():
597 597 raise error.Abort(_('--sourcesort is not supported by this data source')
598 598 )
599 599 if sortmode == 'closesort' and not srcc.hasnativeclose():
600 600 raise error.Abort(_('--closesort is not supported by this data source'))
601 601
602 602 fmap = opts.get('filemap')
603 603 if fmap:
604 604 srcc = filemap.filemap_source(ui, srcc, fmap)
605 605 destc.setfilemapmode(True)
606 606
607 607 if not revmapfile:
608 608 revmapfile = destc.revmapfile()
609 609
610 610 c = converter(ui, srcc, destc, revmapfile, opts)
611 611 c.convert(sortmode)
@@ -1,81 +1,81 b''
1 1 # this is hack to make sure no escape characters are inserted into the output
2 2
3 3 from __future__ import absolute_import
4 4
5 5 import doctest
6 6 import os
7 7 import re
8 8 import sys
9 9
10 10 ispy3 = (sys.version_info[0] >= 3)
11 11
12 12 if 'TERM' in os.environ:
13 13 del os.environ['TERM']
14 14
15 15 class py3docchecker(doctest.OutputChecker):
16 16 def check_output(self, want, got, optionflags):
17 17 want2 = re.sub(r'''\bu(['"])(.*?)\1''', r'\1\2\1', want) # py2: u''
18 18 got2 = re.sub(r'''\bb(['"])(.*?)\1''', r'\1\2\1', got) # py3: b''
19 19 # py3: <exc.name>: b'<msg>' -> <name>: <msg>
20 20 # <exc.name>: <others> -> <name>: <others>
21 21 got2 = re.sub(r'''^mercurial\.\w+\.(\w+): (['"])(.*?)\2''', r'\1: \3',
22 22 got2, re.MULTILINE)
23 23 got2 = re.sub(r'^mercurial\.\w+\.(\w+): ', r'\1: ', got2, re.MULTILINE)
24 24 return any(doctest.OutputChecker.check_output(self, w, g, optionflags)
25 25 for w, g in [(want, got), (want2, got2)])
26 26
27 27 # TODO: migrate doctests to py3 and enable them on both versions
28 28 def testmod(name, optionflags=0, testtarget=None, py2=True, py3=True):
29 29 if not (not ispy3 and py2 or ispy3 and py3):
30 30 return
31 31 __import__(name)
32 32 mod = sys.modules[name]
33 33 if testtarget is not None:
34 34 mod = getattr(mod, testtarget)
35 35
36 36 # minimal copy of doctest.testmod()
37 37 finder = doctest.DocTestFinder()
38 38 checker = None
39 39 if ispy3:
40 40 checker = py3docchecker()
41 41 runner = doctest.DocTestRunner(checker=checker, optionflags=optionflags)
42 42 for test in finder.find(mod, name):
43 43 runner.run(test)
44 44 runner.summarize()
45 45
46 46 testmod('mercurial.changegroup')
47 47 testmod('mercurial.changelog')
48 48 testmod('mercurial.color')
49 49 testmod('mercurial.config')
50 50 testmod('mercurial.context')
51 51 testmod('mercurial.dagparser', optionflags=doctest.NORMALIZE_WHITESPACE)
52 52 testmod('mercurial.dispatch')
53 53 testmod('mercurial.encoding')
54 54 testmod('mercurial.formatter')
55 55 testmod('mercurial.hg')
56 56 testmod('mercurial.hgweb.hgwebdir_mod')
57 57 testmod('mercurial.match')
58 58 testmod('mercurial.mdiff')
59 59 testmod('mercurial.minirst')
60 60 testmod('mercurial.patch')
61 61 testmod('mercurial.pathutil')
62 62 testmod('mercurial.parser')
63 63 testmod('mercurial.pycompat')
64 64 testmod('mercurial.revsetlang')
65 65 testmod('mercurial.smartset')
66 66 testmod('mercurial.store')
67 67 testmod('mercurial.subrepo')
68 68 testmod('mercurial.templatefilters')
69 69 testmod('mercurial.templater')
70 70 testmod('mercurial.ui')
71 71 testmod('mercurial.url')
72 72 testmod('mercurial.util', py3=False) # py3: multiple bytes/unicode issues
73 73 testmod('mercurial.util', testtarget='platform')
74 testmod('hgext.convert.convcmd', py3=False) # py3: use of str() ?
74 testmod('hgext.convert.convcmd')
75 75 testmod('hgext.convert.cvsps')
76 76 testmod('hgext.convert.filemap')
77 77 testmod('hgext.convert.p4')
78 78 testmod('hgext.convert.subversion')
79 79 testmod('hgext.mq')
80 80 # Helper scripts in tests/ that have doctests:
81 81 testmod('drawdag')
General Comments 0
You need to be logged in to leave comments. Login now