comparison contrib/testparseutil.py @ 42330:5364ba1f796f

py3: make contrib/testparseutil.py to work on str(unicodes) contrib/check-code work on unicodes and call functions from testparseutil.py which before this patch used to work on bytes. This path removes that inconsistency and make testparseutil.py work on unicodes. This makes test-check-code.t and test-contrib-check-code.t work on Python 3 again. Differential Revision: https://phab.mercurial-scm.org/D6391
author Pulkit Goyal <7895pulkit@gmail.com>
date Fri, 17 May 2019 00:04:29 +0530
parents 99b4c6d73a72
children 37f38e1dea44
comparison
equal deleted inserted replaced
42329:c7652f7440d9 42330:5364ba1f796f
52 if isinstance(s, builtins.str): 52 if isinstance(s, builtins.str):
53 return s 53 return s
54 return s.decode(u'latin-1') 54 return s.decode(u'latin-1')
55 55
56 def opentext(f): 56 def opentext(f):
57 return open(f, 'rb') 57 return open(f, 'r')
58 else: 58 else:
59 stdin = sys.stdin 59 stdin = sys.stdin
60 stdout = sys.stdout 60 stdout = sys.stdout
61 stderr = sys.stderr 61 stderr = sys.stderr
62 62
162 ... def __init__(self, desc, matchfunc): 162 ... def __init__(self, desc, matchfunc):
163 ... self.desc = desc 163 ... self.desc = desc
164 ... self.matchfunc = matchfunc 164 ... self.matchfunc = matchfunc
165 ... def startsat(self, line): 165 ... def startsat(self, line):
166 ... return self.matchfunc(line) 166 ... return self.matchfunc(line)
167 >>> ambig1 = ambigmatcher(b'ambiguous #1', 167 >>> ambig1 = ambigmatcher('ambiguous #1',
168 ... lambda l: l.startswith(b' $ cat ')) 168 ... lambda l: l.startswith(' $ cat '))
169 >>> ambig2 = ambigmatcher(b'ambiguous #2', 169 >>> ambig2 = ambigmatcher('ambiguous #2',
170 ... lambda l: l.endswith(b'<< EOF\\n')) 170 ... lambda l: l.endswith('<< EOF\\n'))
171 >>> lines = [b' $ cat > foo.py << EOF\\n'] 171 >>> lines = [' $ cat > foo.py << EOF\\n']
172 >>> errors = [] 172 >>> errors = []
173 >>> matchers = [ambig1, ambig2] 173 >>> matchers = [ambig1, ambig2]
174 >>> list(t for t in embedded(b'<dummy>', lines, errors, matchers)) 174 >>> list(t for t in embedded('<dummy>', lines, errors, matchers))
175 [] 175 []
176 >>> b2s(errors) 176 >>> b2s(errors)
177 ['<dummy>:1: ambiguous line for "ambiguous #1", "ambiguous #2"'] 177 ['<dummy>:1: ambiguous line for "ambiguous #1", "ambiguous #2"']
178 178
179 """ 179 """
180 matcher = None 180 matcher = None
181 ctx = filename = code = startline = None # for pyflakes 181 ctx = filename = code = startline = None # for pyflakes
182 182
183 for lineno, line in enumerate(lines, 1): 183 for lineno, line in enumerate(lines, 1):
184 if not line.endswith(b'\n'): 184 if not line.endswith('\n'):
185 line += b'\n' # to normalize EOF line 185 line += '\n' # to normalize EOF line
186 if matcher: # now, inside embedded code 186 if matcher: # now, inside embedded code
187 if matcher.endsat(ctx, line): 187 if matcher.endsat(ctx, line):
188 codeatend = matcher.codeatend(ctx, line) 188 codeatend = matcher.codeatend(ctx, line)
189 if codeatend is not None: 189 if codeatend is not None:
190 code.append(codeatend) 190 code.append(codeatend)
191 if not matcher.ignores(ctx): 191 if not matcher.ignores(ctx):
192 yield (filename, startline, lineno, b''.join(code)) 192 yield (filename, startline, lineno, ''.join(code))
193 matcher = None 193 matcher = None
194 # DO NOT "continue", because line might start next fragment 194 # DO NOT "continue", because line might start next fragment
195 elif not matcher.isinside(ctx, line): 195 elif not matcher.isinside(ctx, line):
196 # this is an error of basefile 196 # this is an error of basefile
197 # (if matchers are implemented correctly) 197 # (if matchers are implemented correctly)
198 errors.append(b'%s:%d: unexpected line for "%s"' 198 errors.append('%s:%d: unexpected line for "%s"'
199 % (basefile, lineno, matcher.desc)) 199 % (basefile, lineno, matcher.desc))
200 # stop extracting embedded code by current 'matcher', 200 # stop extracting embedded code by current 'matcher',
201 # because appearance of unexpected line might mean 201 # because appearance of unexpected line might mean
202 # that expected end-of-embedded-code line might never 202 # that expected end-of-embedded-code line might never
203 # appear 203 # appear
216 if ctx: 216 if ctx:
217 matched.append((m, ctx)) 217 matched.append((m, ctx))
218 if matched: 218 if matched:
219 if len(matched) > 1: 219 if len(matched) > 1:
220 # this is an error of matchers, maybe 220 # this is an error of matchers, maybe
221 errors.append(b'%s:%d: ambiguous line for %s' % 221 errors.append('%s:%d: ambiguous line for %s' %
222 (basefile, lineno, 222 (basefile, lineno,
223 b', '.join([b'"%s"' % m.desc 223 ', '.join(['"%s"' % m.desc
224 for m, c in matched]))) 224 for m, c in matched])))
225 # omit extracting embedded code, because choosing 225 # omit extracting embedded code, because choosing
226 # arbitrary matcher from matched ones might fail to 226 # arbitrary matcher from matched ones might fail to
227 # detect the end of embedded code as expected. 227 # detect the end of embedded code as expected.
228 continue 228 continue
237 startline = lineno + 1 237 startline = lineno + 1
238 238
239 if matcher: 239 if matcher:
240 # examine whether EOF ends embedded code, because embedded 240 # examine whether EOF ends embedded code, because embedded
241 # code isn't yet ended explicitly 241 # code isn't yet ended explicitly
242 if matcher.endsat(ctx, b'\n'): 242 if matcher.endsat(ctx, '\n'):
243 codeatend = matcher.codeatend(ctx, b'\n') 243 codeatend = matcher.codeatend(ctx, '\n')
244 if codeatend is not None: 244 if codeatend is not None:
245 code.append(codeatend) 245 code.append(codeatend)
246 if not matcher.ignores(ctx): 246 if not matcher.ignores(ctx):
247 yield (filename, startline, lineno + 1, b''.join(code)) 247 yield (filename, startline, lineno + 1, ''.join(code))
248 else: 248 else:
249 # this is an error of basefile 249 # this is an error of basefile
250 # (if matchers are implemented correctly) 250 # (if matchers are implemented correctly)
251 errors.append(b'%s:%d: unexpected end of file for "%s"' 251 errors.append('%s:%d: unexpected end of file for "%s"'
252 % (basefile, lineno, matcher.desc)) 252 % (basefile, lineno, matcher.desc))
253 253
254 # heredoc limit mark to ignore embedded code at check-code.py or so 254 # heredoc limit mark to ignore embedded code at check-code.py or so
255 heredocignorelimit = b'NO_CHECK_EOF' 255 heredocignorelimit = 'NO_CHECK_EOF'
256 256
257 # the pattern to match against cases below, and to return a limit mark 257 # the pattern to match against cases below, and to return a limit mark
258 # string as 'lname' group 258 # string as 'lname' group
259 # 259 #
260 # - << LIMITMARK 260 # - << LIMITMARK
261 # - << "LIMITMARK" 261 # - << "LIMITMARK"
262 # - << 'LIMITMARK' 262 # - << 'LIMITMARK'
263 heredoclimitpat = br'\s*<<\s*(?P<lquote>["\']?)(?P<limit>\w+)(?P=lquote)' 263 heredoclimitpat = r'\s*<<\s*(?P<lquote>["\']?)(?P<limit>\w+)(?P=lquote)'
264 264
265 class fileheredocmatcher(embeddedmatcher): 265 class fileheredocmatcher(embeddedmatcher):
266 """Detect "cat > FILE << LIMIT" style embedded code 266 """Detect "cat > FILE << LIMIT" style embedded code
267 267
268 >>> matcher = fileheredocmatcher(b'heredoc .py file', br'[^<]+\\.py') 268 >>> matcher = fileheredocmatcher(b'heredoc .py file', br'[^<]+\\.py')
269 >>> b2s(matcher.startsat(b' $ cat > file.py << EOF\\n')) 269 >>> b2s(matcher.startsat(' $ cat > file.py << EOF\\n'))
270 ('file.py', ' > EOF\\n') 270 ('file.py', ' > EOF\\n')
271 >>> b2s(matcher.startsat(b' $ cat >>file.py <<EOF\\n')) 271 >>> b2s(matcher.startsat(' $ cat >>file.py <<EOF\\n'))
272 ('file.py', ' > EOF\\n') 272 ('file.py', ' > EOF\\n')
273 >>> b2s(matcher.startsat(b' $ cat> \\x27any file.py\\x27<< "EOF"\\n')) 273 >>> b2s(matcher.startsat(' $ cat> \\x27any file.py\\x27<< "EOF"\\n'))
274 ('any file.py', ' > EOF\\n') 274 ('any file.py', ' > EOF\\n')
275 >>> b2s(matcher.startsat(b" $ cat > file.py << 'ANYLIMIT'\\n")) 275 >>> b2s(matcher.startsat(" $ cat > file.py << 'ANYLIMIT'\\n"))
276 ('file.py', ' > ANYLIMIT\\n') 276 ('file.py', ' > ANYLIMIT\\n')
277 >>> b2s(matcher.startsat(b' $ cat<<ANYLIMIT>"file.py"\\n')) 277 >>> b2s(matcher.startsat(' $ cat<<ANYLIMIT>"file.py"\\n'))
278 ('file.py', ' > ANYLIMIT\\n') 278 ('file.py', ' > ANYLIMIT\\n')
279 >>> start = b' $ cat > file.py << EOF\\n' 279 >>> start = ' $ cat > file.py << EOF\\n'
280 >>> ctx = matcher.startsat(start) 280 >>> ctx = matcher.startsat(start)
281 >>> matcher.codeatstart(ctx, start) 281 >>> matcher.codeatstart(ctx, start)
282 >>> b2s(matcher.filename(ctx)) 282 >>> b2s(matcher.filename(ctx))
283 'file.py' 283 'file.py'
284 >>> matcher.ignores(ctx) 284 >>> matcher.ignores(ctx)
285 False 285 False
286 >>> inside = b' > foo = 1\\n' 286 >>> inside = ' > foo = 1\\n'
287 >>> matcher.endsat(ctx, inside) 287 >>> matcher.endsat(ctx, inside)
288 False 288 False
289 >>> matcher.isinside(ctx, inside) 289 >>> matcher.isinside(ctx, inside)
290 True 290 True
291 >>> b2s(matcher.codeinside(ctx, inside)) 291 >>> b2s(matcher.codeinside(ctx, inside))
292 'foo = 1\\n' 292 'foo = 1\\n'
293 >>> end = b' > EOF\\n' 293 >>> end = ' > EOF\\n'
294 >>> matcher.endsat(ctx, end) 294 >>> matcher.endsat(ctx, end)
295 True 295 True
296 >>> matcher.codeatend(ctx, end) 296 >>> matcher.codeatend(ctx, end)
297 >>> matcher.endsat(ctx, b' > EOFEOF\\n') 297 >>> matcher.endsat(ctx, ' > EOFEOF\\n')
298 False 298 False
299 >>> ctx = matcher.startsat(b' $ cat > file.py << NO_CHECK_EOF\\n') 299 >>> ctx = matcher.startsat(' $ cat > file.py << NO_CHECK_EOF\\n')
300 >>> matcher.ignores(ctx) 300 >>> matcher.ignores(ctx)
301 True 301 True
302 """ 302 """
303 _prefix = b' > ' 303 _prefix = ' > '
304 304
305 def __init__(self, desc, namepat): 305 def __init__(self, desc, namepat):
306 super(fileheredocmatcher, self).__init__(desc) 306 super(fileheredocmatcher, self).__init__(desc)
307 307
308 # build the pattern to match against cases below (and ">>" 308 # build the pattern to match against cases below (and ">>"
310 # group 310 # group
311 # 311 #
312 # - > NAMEPAT 312 # - > NAMEPAT
313 # - > "NAMEPAT" 313 # - > "NAMEPAT"
314 # - > 'NAMEPAT' 314 # - > 'NAMEPAT'
315 namepat = (br'\s*>>?\s*(?P<nquote>["\']?)(?P<name>%s)(?P=nquote)' 315 namepat = (r'\s*>>?\s*(?P<nquote>["\']?)(?P<name>%s)(?P=nquote)'
316 % namepat) 316 % namepat)
317 self._fileres = [ 317 self._fileres = [
318 # "cat > NAME << LIMIT" case 318 # "cat > NAME << LIMIT" case
319 re.compile(br' \$ \s*cat' + namepat + heredoclimitpat), 319 re.compile(r' \$ \s*cat' + namepat + heredoclimitpat),
320 # "cat << LIMIT > NAME" case 320 # "cat << LIMIT > NAME" case
321 re.compile(br' \$ \s*cat' + heredoclimitpat + namepat), 321 re.compile(r' \$ \s*cat' + heredoclimitpat + namepat),
322 ] 322 ]
323 323
324 def startsat(self, line): 324 def startsat(self, line):
325 # ctx is (filename, END-LINE-OF-EMBEDDED-CODE) tuple 325 # ctx is (filename, END-LINE-OF-EMBEDDED-CODE) tuple
326 for filere in self._fileres: 326 for filere in self._fileres:
327 matched = filere.match(line) 327 matched = filere.match(line)
328 if matched: 328 if matched:
329 return (matched.group('name'), 329 return (matched.group('name'),
330 b' > %s\n' % matched.group('limit')) 330 ' > %s\n' % matched.group('limit'))
331 331
332 def endsat(self, ctx, line): 332 def endsat(self, ctx, line):
333 return ctx[1] == line 333 return ctx[1] == line
334 334
335 def isinside(self, ctx, line): 335 def isinside(self, ctx, line):
336 return line.startswith(self._prefix) 336 return line.startswith(self._prefix)
337 337
338 def ignores(self, ctx): 338 def ignores(self, ctx):
339 return b' > %s\n' % heredocignorelimit == ctx[1] 339 return ' > %s\n' % heredocignorelimit == ctx[1]
340 340
341 def filename(self, ctx): 341 def filename(self, ctx):
342 return ctx[0] 342 return ctx[0]
343 343
344 def codeatstart(self, ctx, line): 344 def codeatstart(self, ctx, line):
355 355
356 class pydoctestmatcher(embeddedmatcher): 356 class pydoctestmatcher(embeddedmatcher):
357 """Detect ">>> code" style embedded python code 357 """Detect ">>> code" style embedded python code
358 358
359 >>> matcher = pydoctestmatcher() 359 >>> matcher = pydoctestmatcher()
360 >>> startline = b' >>> foo = 1\\n' 360 >>> startline = ' >>> foo = 1\\n'
361 >>> matcher.startsat(startline) 361 >>> matcher.startsat(startline)
362 True 362 True
363 >>> matcher.startsat(b' ... foo = 1\\n') 363 >>> matcher.startsat(' ... foo = 1\\n')
364 False 364 False
365 >>> ctx = matcher.startsat(startline) 365 >>> ctx = matcher.startsat(startline)
366 >>> matcher.filename(ctx) 366 >>> matcher.filename(ctx)
367 >>> matcher.ignores(ctx) 367 >>> matcher.ignores(ctx)
368 False 368 False
369 >>> b2s(matcher.codeatstart(ctx, startline)) 369 >>> b2s(matcher.codeatstart(ctx, startline))
370 'foo = 1\\n' 370 'foo = 1\\n'
371 >>> inside = b' >>> foo = 1\\n' 371 >>> inside = ' >>> foo = 1\\n'
372 >>> matcher.endsat(ctx, inside) 372 >>> matcher.endsat(ctx, inside)
373 False 373 False
374 >>> matcher.isinside(ctx, inside) 374 >>> matcher.isinside(ctx, inside)
375 True 375 True
376 >>> b2s(matcher.codeinside(ctx, inside)) 376 >>> b2s(matcher.codeinside(ctx, inside))
377 'foo = 1\\n' 377 'foo = 1\\n'
378 >>> inside = b' ... foo = 1\\n' 378 >>> inside = ' ... foo = 1\\n'
379 >>> matcher.endsat(ctx, inside) 379 >>> matcher.endsat(ctx, inside)
380 False 380 False
381 >>> matcher.isinside(ctx, inside) 381 >>> matcher.isinside(ctx, inside)
382 True 382 True
383 >>> b2s(matcher.codeinside(ctx, inside)) 383 >>> b2s(matcher.codeinside(ctx, inside))
384 'foo = 1\\n' 384 'foo = 1\\n'
385 >>> inside = b' expected output\\n' 385 >>> inside = ' expected output\\n'
386 >>> matcher.endsat(ctx, inside) 386 >>> matcher.endsat(ctx, inside)
387 False 387 False
388 >>> matcher.isinside(ctx, inside) 388 >>> matcher.isinside(ctx, inside)
389 True 389 True
390 >>> b2s(matcher.codeinside(ctx, inside)) 390 >>> b2s(matcher.codeinside(ctx, inside))
391 '\\n' 391 '\\n'
392 >>> inside = b' \\n' 392 >>> inside = ' \\n'
393 >>> matcher.endsat(ctx, inside) 393 >>> matcher.endsat(ctx, inside)
394 False 394 False
395 >>> matcher.isinside(ctx, inside) 395 >>> matcher.isinside(ctx, inside)
396 True 396 True
397 >>> b2s(matcher.codeinside(ctx, inside)) 397 >>> b2s(matcher.codeinside(ctx, inside))
398 '\\n' 398 '\\n'
399 >>> end = b' $ foo bar\\n' 399 >>> end = ' $ foo bar\\n'
400 >>> matcher.endsat(ctx, end) 400 >>> matcher.endsat(ctx, end)
401 True 401 True
402 >>> matcher.codeatend(ctx, end) 402 >>> matcher.codeatend(ctx, end)
403 >>> end = b'\\n' 403 >>> end = '\\n'
404 >>> matcher.endsat(ctx, end) 404 >>> matcher.endsat(ctx, end)
405 True 405 True
406 >>> matcher.codeatend(ctx, end) 406 >>> matcher.codeatend(ctx, end)
407 """ 407 """
408 _prefix = b' >>> ' 408 _prefix = ' >>> '
409 _prefixre = re.compile(br' (>>>|\.\.\.) ') 409 _prefixre = re.compile(r' (>>>|\.\.\.) ')
410 410
411 # If a line matches against not _prefixre but _outputre, that line 411 # If a line matches against not _prefixre but _outputre, that line
412 # is "an expected output line" (= not a part of code fragment). 412 # is "an expected output line" (= not a part of code fragment).
413 # 413 #
414 # Strictly speaking, a line matching against "(#if|#else|#endif)" 414 # Strictly speaking, a line matching against "(#if|#else|#endif)"
415 # is also treated similarly in "inline python code" semantics by 415 # is also treated similarly in "inline python code" semantics by
416 # run-tests.py. But "directive line inside inline python code" 416 # run-tests.py. But "directive line inside inline python code"
417 # should be rejected by Mercurial reviewers. Therefore, this 417 # should be rejected by Mercurial reviewers. Therefore, this
418 # regexp does not matche against such directive lines. 418 # regexp does not matche against such directive lines.
419 _outputre = re.compile(br' $| [^$]') 419 _outputre = re.compile(r' $| [^$]')
420 420
421 def __init__(self): 421 def __init__(self):
422 super(pydoctestmatcher, self).__init__(b"doctest style python code") 422 super(pydoctestmatcher, self).__init__("doctest style python code")
423 423
424 def startsat(self, line): 424 def startsat(self, line):
425 # ctx is "True" 425 # ctx is "True"
426 return line.startswith(self._prefix) 426 return line.startswith(self._prefix)
427 427
444 return None # no embedded code at end line 444 return None # no embedded code at end line
445 445
446 def codeinside(self, ctx, line): 446 def codeinside(self, ctx, line):
447 if self._prefixre.match(line): 447 if self._prefixre.match(line):
448 return line[len(self._prefix):] # strip prefix ' >>> '/' ... ' 448 return line[len(self._prefix):] # strip prefix ' >>> '/' ... '
449 return b'\n' # an expected output line is treated as an empty line 449 return '\n' # an expected output line is treated as an empty line
450 450
451 class pyheredocmatcher(embeddedmatcher): 451 class pyheredocmatcher(embeddedmatcher):
452 """Detect "python << LIMIT" style embedded python code 452 """Detect "python << LIMIT" style embedded python code
453 453
454 >>> matcher = pyheredocmatcher() 454 >>> matcher = pyheredocmatcher()
455 >>> b2s(matcher.startsat(b' $ python << EOF\\n')) 455 >>> b2s(matcher.startsat(' $ python << EOF\\n'))
456 ' > EOF\\n' 456 ' > EOF\\n'
457 >>> b2s(matcher.startsat(b' $ $PYTHON <<EOF\\n')) 457 >>> b2s(matcher.startsat(' $ $PYTHON <<EOF\\n'))
458 ' > EOF\\n' 458 ' > EOF\\n'
459 >>> b2s(matcher.startsat(b' $ "$PYTHON"<< "EOF"\\n')) 459 >>> b2s(matcher.startsat(' $ "$PYTHON"<< "EOF"\\n'))
460 ' > EOF\\n' 460 ' > EOF\\n'
461 >>> b2s(matcher.startsat(b" $ $PYTHON << 'ANYLIMIT'\\n")) 461 >>> b2s(matcher.startsat(" $ $PYTHON << 'ANYLIMIT'\\n"))
462 ' > ANYLIMIT\\n' 462 ' > ANYLIMIT\\n'
463 >>> matcher.startsat(b' $ "$PYTHON" < EOF\\n') 463 >>> matcher.startsat(' $ "$PYTHON" < EOF\\n')
464 >>> start = b' $ python << EOF\\n' 464 >>> start = ' $ python << EOF\\n'
465 >>> ctx = matcher.startsat(start) 465 >>> ctx = matcher.startsat(start)
466 >>> matcher.codeatstart(ctx, start) 466 >>> matcher.codeatstart(ctx, start)
467 >>> matcher.filename(ctx) 467 >>> matcher.filename(ctx)
468 >>> matcher.ignores(ctx) 468 >>> matcher.ignores(ctx)
469 False 469 False
470 >>> inside = b' > foo = 1\\n' 470 >>> inside = ' > foo = 1\\n'
471 >>> matcher.endsat(ctx, inside) 471 >>> matcher.endsat(ctx, inside)
472 False 472 False
473 >>> matcher.isinside(ctx, inside) 473 >>> matcher.isinside(ctx, inside)
474 True 474 True
475 >>> b2s(matcher.codeinside(ctx, inside)) 475 >>> b2s(matcher.codeinside(ctx, inside))
476 'foo = 1\\n' 476 'foo = 1\\n'
477 >>> end = b' > EOF\\n' 477 >>> end = ' > EOF\\n'
478 >>> matcher.endsat(ctx, end) 478 >>> matcher.endsat(ctx, end)
479 True 479 True
480 >>> matcher.codeatend(ctx, end) 480 >>> matcher.codeatend(ctx, end)
481 >>> matcher.endsat(ctx, b' > EOFEOF\\n') 481 >>> matcher.endsat(ctx, ' > EOFEOF\\n')
482 False 482 False
483 >>> ctx = matcher.startsat(b' $ python << NO_CHECK_EOF\\n') 483 >>> ctx = matcher.startsat(' $ python << NO_CHECK_EOF\\n')
484 >>> matcher.ignores(ctx) 484 >>> matcher.ignores(ctx)
485 True 485 True
486 """ 486 """
487 _prefix = b' > ' 487 _prefix = ' > '
488 488
489 _startre = re.compile(br' \$ (\$PYTHON|"\$PYTHON"|python).*' + 489 _startre = re.compile(r' \$ (\$PYTHON|"\$PYTHON"|python).*' +
490 heredoclimitpat) 490 heredoclimitpat)
491 491
492 def __init__(self): 492 def __init__(self):
493 super(pyheredocmatcher, self).__init__(b"heredoc python invocation") 493 super(pyheredocmatcher, self).__init__("heredoc python invocation")
494 494
495 def startsat(self, line): 495 def startsat(self, line):
496 # ctx is END-LINE-OF-EMBEDDED-CODE 496 # ctx is END-LINE-OF-EMBEDDED-CODE
497 matched = self._startre.match(line) 497 matched = self._startre.match(line)
498 if matched: 498 if matched:
499 return b' > %s\n' % matched.group('limit') 499 return ' > %s\n' % matched.group('limit')
500 500
501 def endsat(self, ctx, line): 501 def endsat(self, ctx, line):
502 return ctx == line 502 return ctx == line
503 503
504 def isinside(self, ctx, line): 504 def isinside(self, ctx, line):
505 return line.startswith(self._prefix) 505 return line.startswith(self._prefix)
506 506
507 def ignores(self, ctx): 507 def ignores(self, ctx):
508 return b' > %s\n' % heredocignorelimit == ctx 508 return ' > %s\n' % heredocignorelimit == ctx
509 509
510 def filename(self, ctx): 510 def filename(self, ctx):
511 return None # no filename 511 return None # no filename
512 512
513 def codeatstart(self, ctx, line): 513 def codeatstart(self, ctx, line):
522 _pymatchers = [ 522 _pymatchers = [
523 pydoctestmatcher(), 523 pydoctestmatcher(),
524 pyheredocmatcher(), 524 pyheredocmatcher(),
525 # use '[^<]+' instead of '\S+', in order to match against 525 # use '[^<]+' instead of '\S+', in order to match against
526 # paths including whitespaces 526 # paths including whitespaces
527 fileheredocmatcher(b'heredoc .py file', br'[^<]+\.py'), 527 fileheredocmatcher('heredoc .py file', r'[^<]+\.py'),
528 ] 528 ]
529 529
530 def pyembedded(basefile, lines, errors): 530 def pyembedded(basefile, lines, errors):
531 return embedded(basefile, lines, errors, _pymatchers) 531 return embedded(basefile, lines, errors, _pymatchers)
532 532
534 # for embedded shell script 534 # for embedded shell script
535 535
536 _shmatchers = [ 536 _shmatchers = [
537 # use '[^<]+' instead of '\S+', in order to match against 537 # use '[^<]+' instead of '\S+', in order to match against
538 # paths including whitespaces 538 # paths including whitespaces
539 fileheredocmatcher(b'heredoc .sh file', br'[^<]+\.sh'), 539 fileheredocmatcher('heredoc .sh file', r'[^<]+\.sh'),
540 ] 540 ]
541 541
542 def shembedded(basefile, lines, errors): 542 def shembedded(basefile, lines, errors):
543 return embedded(basefile, lines, errors, _shmatchers) 543 return embedded(basefile, lines, errors, _shmatchers)
544 544
546 # for embedded hgrc configuration 546 # for embedded hgrc configuration
547 547
548 _hgrcmatchers = [ 548 _hgrcmatchers = [
549 # use '[^<]+' instead of '\S+', in order to match against 549 # use '[^<]+' instead of '\S+', in order to match against
550 # paths including whitespaces 550 # paths including whitespaces
551 fileheredocmatcher(b'heredoc hgrc file', 551 fileheredocmatcher('heredoc hgrc file',
552 br'(([^/<]+/)+hgrc|\$HGRCPATH|\${HGRCPATH})'), 552 r'(([^/<]+/)+hgrc|\$HGRCPATH|\${HGRCPATH})'),
553 ] 553 ]
554 554
555 def hgrcembedded(basefile, lines, errors): 555 def hgrcembedded(basefile, lines, errors):
556 return embedded(basefile, lines, errors, _hgrcmatchers) 556 return embedded(basefile, lines, errors, _hgrcmatchers)
557 557
563 563
564 def showembedded(basefile, lines, embeddedfunc, opts): 564 def showembedded(basefile, lines, embeddedfunc, opts):
565 errors = [] 565 errors = []
566 for name, starts, ends, code in embeddedfunc(basefile, lines, errors): 566 for name, starts, ends, code in embeddedfunc(basefile, lines, errors):
567 if not name: 567 if not name:
568 name = b'<anonymous>' 568 name = '<anonymous>'
569 writeout(b"%s:%d: %s starts\n" % (basefile, starts, name)) 569 writeout("%s:%d: %s starts\n" % (basefile, starts, name))
570 if opts.verbose and code: 570 if opts.verbose and code:
571 writeout(b" |%s\n" % 571 writeout(" |%s\n" %
572 b"\n |".join(l for l in code.splitlines())) 572 "\n |".join(l for l in code.splitlines()))
573 writeout(b"%s:%d: %s ends\n" % (basefile, ends, name)) 573 writeout("%s:%d: %s ends\n" % (basefile, ends, name))
574 for e in errors: 574 for e in errors:
575 writeerr(b"%s\n" % e) 575 writeerr("%s\n" % e)
576 return len(errors) 576 return len(errors)
577 577
578 def applyembedded(args, embeddedfunc, opts): 578 def applyembedded(args, embeddedfunc, opts):
579 ret = 0 579 ret = 0
580 if args: 580 if args:
581 for f in args: 581 for f in args:
582 with opentext(f) as fp: 582 with opentext(f) as fp:
583 if showembedded(bytestr(f), fp, embeddedfunc, opts): 583 if showembedded(f, fp, embeddedfunc, opts):
584 ret = 1 584 ret = 1
585 else: 585 else:
586 lines = [l for l in stdin.readlines()] 586 lines = [l for l in stdin.readlines()]
587 if showembedded(b'<stdin>', lines, embeddedfunc, opts): 587 if showembedded('<stdin>', lines, embeddedfunc, opts):
588 ret = 1 588 ret = 1
589 return ret 589 return ret
590 590
591 commands = {} 591 commands = {}
592 def command(name, desc): 592 def command(name, desc):