47 return s |
51 return s |
48 return s.decode(u'latin-1') |
52 return s.decode(u'latin-1') |
49 |
53 |
50 def opentext(f): |
54 def opentext(f): |
51 return open(f, 'r') |
55 return open(f, 'r') |
|
56 |
|
57 |
52 else: |
58 else: |
53 bytestr = str |
59 bytestr = str |
54 sysstr = identity |
60 sysstr = identity |
55 |
61 |
56 opentext = open |
62 opentext = open |
57 |
63 |
|
64 |
58 def b2s(x): |
65 def b2s(x): |
59 # convert BYTES elements in "x" to SYSSTR recursively |
66 # convert BYTES elements in "x" to SYSSTR recursively |
60 return rapply(sysstr, x) |
67 return rapply(sysstr, x) |
61 |
68 |
|
69 |
62 def writeout(data): |
70 def writeout(data): |
63 # write "data" in BYTES into stdout |
71 # write "data" in BYTES into stdout |
64 sys.stdout.write(data) |
72 sys.stdout.write(data) |
65 |
73 |
|
74 |
66 def writeerr(data): |
75 def writeerr(data): |
67 # write "data" in BYTES into stderr |
76 # write "data" in BYTES into stderr |
68 sys.stderr.write(data) |
77 sys.stderr.write(data) |
69 |
78 |
|
79 |
70 #################### |
80 #################### |
|
81 |
71 |
82 |
72 class embeddedmatcher(object): |
83 class embeddedmatcher(object): |
73 """Base class to detect embedded code fragments in *.t test script |
84 """Base class to detect embedded code fragments in *.t test script |
74 """ |
85 """ |
|
86 |
75 __metaclass__ = abc.ABCMeta |
87 __metaclass__ = abc.ABCMeta |
76 |
88 |
77 def __init__(self, desc): |
89 def __init__(self, desc): |
78 self.desc = desc |
90 self.desc = desc |
79 |
91 |
166 >>> b2s(errors) |
179 >>> b2s(errors) |
167 ['<dummy>:1: ambiguous line for "ambiguous #1", "ambiguous #2"'] |
180 ['<dummy>:1: ambiguous line for "ambiguous #1", "ambiguous #2"'] |
168 |
181 |
169 """ |
182 """ |
170 matcher = None |
183 matcher = None |
171 ctx = filename = code = startline = None # for pyflakes |
184 ctx = filename = code = startline = None # for pyflakes |
172 |
185 |
173 for lineno, line in enumerate(lines, 1): |
186 for lineno, line in enumerate(lines, 1): |
174 if not line.endswith('\n'): |
187 if not line.endswith('\n'): |
175 line += '\n' # to normalize EOF line |
188 line += '\n' # to normalize EOF line |
176 if matcher: # now, inside embedded code |
189 if matcher: # now, inside embedded code |
177 if matcher.endsat(ctx, line): |
190 if matcher.endsat(ctx, line): |
178 codeatend = matcher.codeatend(ctx, line) |
191 codeatend = matcher.codeatend(ctx, line) |
179 if codeatend is not None: |
192 if codeatend is not None: |
180 code.append(codeatend) |
193 code.append(codeatend) |
181 if not matcher.ignores(ctx): |
194 if not matcher.ignores(ctx): |
183 matcher = None |
196 matcher = None |
184 # DO NOT "continue", because line might start next fragment |
197 # DO NOT "continue", because line might start next fragment |
185 elif not matcher.isinside(ctx, line): |
198 elif not matcher.isinside(ctx, line): |
186 # this is an error of basefile |
199 # this is an error of basefile |
187 # (if matchers are implemented correctly) |
200 # (if matchers are implemented correctly) |
188 errors.append('%s:%d: unexpected line for "%s"' |
201 errors.append( |
189 % (basefile, lineno, matcher.desc)) |
202 '%s:%d: unexpected line for "%s"' |
|
203 % (basefile, lineno, matcher.desc) |
|
204 ) |
190 # stop extracting embedded code by current 'matcher', |
205 # stop extracting embedded code by current 'matcher', |
191 # because appearance of unexpected line might mean |
206 # because appearance of unexpected line might mean |
192 # that expected end-of-embedded-code line might never |
207 # that expected end-of-embedded-code line might never |
193 # appear |
208 # appear |
194 matcher = None |
209 matcher = None |
206 if ctx: |
221 if ctx: |
207 matched.append((m, ctx)) |
222 matched.append((m, ctx)) |
208 if matched: |
223 if matched: |
209 if len(matched) > 1: |
224 if len(matched) > 1: |
210 # this is an error of matchers, maybe |
225 # this is an error of matchers, maybe |
211 errors.append('%s:%d: ambiguous line for %s' % |
226 errors.append( |
212 (basefile, lineno, |
227 '%s:%d: ambiguous line for %s' |
213 ', '.join(['"%s"' % m.desc |
228 % ( |
214 for m, c in matched]))) |
229 basefile, |
|
230 lineno, |
|
231 ', '.join(['"%s"' % m.desc for m, c in matched]), |
|
232 ) |
|
233 ) |
215 # omit extracting embedded code, because choosing |
234 # omit extracting embedded code, because choosing |
216 # arbitrary matcher from matched ones might fail to |
235 # arbitrary matcher from matched ones might fail to |
217 # detect the end of embedded code as expected. |
236 # detect the end of embedded code as expected. |
218 continue |
237 continue |
219 matcher, ctx = matched[0] |
238 matcher, ctx = matched[0] |
236 if not matcher.ignores(ctx): |
255 if not matcher.ignores(ctx): |
237 yield (filename, startline, lineno + 1, ''.join(code)) |
256 yield (filename, startline, lineno + 1, ''.join(code)) |
238 else: |
257 else: |
239 # this is an error of basefile |
258 # this is an error of basefile |
240 # (if matchers are implemented correctly) |
259 # (if matchers are implemented correctly) |
241 errors.append('%s:%d: unexpected end of file for "%s"' |
260 errors.append( |
242 % (basefile, lineno, matcher.desc)) |
261 '%s:%d: unexpected end of file for "%s"' |
|
262 % (basefile, lineno, matcher.desc) |
|
263 ) |
|
264 |
243 |
265 |
244 # heredoc limit mark to ignore embedded code at check-code.py or so |
266 # heredoc limit mark to ignore embedded code at check-code.py or so |
245 heredocignorelimit = 'NO_CHECK_EOF' |
267 heredocignorelimit = 'NO_CHECK_EOF' |
246 |
268 |
247 # the pattern to match against cases below, and to return a limit mark |
269 # the pattern to match against cases below, and to return a limit mark |
314 def startsat(self, line): |
339 def startsat(self, line): |
315 # ctx is (filename, END-LINE-OF-EMBEDDED-CODE) tuple |
340 # ctx is (filename, END-LINE-OF-EMBEDDED-CODE) tuple |
316 for filere in self._fileres: |
341 for filere in self._fileres: |
317 matched = filere.match(line) |
342 matched = filere.match(line) |
318 if matched: |
343 if matched: |
319 return (matched.group('name'), |
344 return ( |
320 ' > %s\n' % matched.group('limit')) |
345 matched.group('name'), |
|
346 ' > %s\n' % matched.group('limit'), |
|
347 ) |
321 |
348 |
322 def endsat(self, ctx, line): |
349 def endsat(self, ctx, line): |
323 return ctx[1] == line |
350 return ctx[1] == line |
324 |
351 |
325 def isinside(self, ctx, line): |
352 def isinside(self, ctx, line): |
330 |
357 |
331 def filename(self, ctx): |
358 def filename(self, ctx): |
332 return ctx[0] |
359 return ctx[0] |
333 |
360 |
334 def codeatstart(self, ctx, line): |
361 def codeatstart(self, ctx, line): |
335 return None # no embedded code at start line |
362 return None # no embedded code at start line |
336 |
363 |
337 def codeatend(self, ctx, line): |
364 def codeatend(self, ctx, line): |
338 return None # no embedded code at end line |
365 return None # no embedded code at end line |
339 |
366 |
340 def codeinside(self, ctx, line): |
367 def codeinside(self, ctx, line): |
341 return line[len(self._prefix):] # strip prefix |
368 return line[len(self._prefix) :] # strip prefix |
|
369 |
342 |
370 |
343 #### |
371 #### |
344 # for embedded python script |
372 # for embedded python script |
|
373 |
345 |
374 |
346 class pydoctestmatcher(embeddedmatcher): |
375 class pydoctestmatcher(embeddedmatcher): |
347 """Detect ">>> code" style embedded python code |
376 """Detect ">>> code" style embedded python code |
348 |
377 |
349 >>> matcher = pydoctestmatcher() |
378 >>> matcher = pydoctestmatcher() |
417 |
447 |
418 def endsat(self, ctx, line): |
448 def endsat(self, ctx, line): |
419 return not (self._prefixre.match(line) or self._outputre.match(line)) |
449 return not (self._prefixre.match(line) or self._outputre.match(line)) |
420 |
450 |
421 def isinside(self, ctx, line): |
451 def isinside(self, ctx, line): |
422 return True # always true, if not yet ended |
452 return True # always true, if not yet ended |
423 |
453 |
424 def ignores(self, ctx): |
454 def ignores(self, ctx): |
425 return False # should be checked always |
455 return False # should be checked always |
426 |
456 |
427 def filename(self, ctx): |
457 def filename(self, ctx): |
428 return None # no filename |
458 return None # no filename |
429 |
459 |
430 def codeatstart(self, ctx, line): |
460 def codeatstart(self, ctx, line): |
431 return line[len(self._prefix):] # strip prefix ' >>> '/' ... ' |
461 return line[len(self._prefix) :] # strip prefix ' >>> '/' ... ' |
432 |
462 |
433 def codeatend(self, ctx, line): |
463 def codeatend(self, ctx, line): |
434 return None # no embedded code at end line |
464 return None # no embedded code at end line |
435 |
465 |
436 def codeinside(self, ctx, line): |
466 def codeinside(self, ctx, line): |
437 if self._prefixre.match(line): |
467 if self._prefixre.match(line): |
438 return line[len(self._prefix):] # strip prefix ' >>> '/' ... ' |
468 return line[len(self._prefix) :] # strip prefix ' >>> '/' ... ' |
439 return '\n' # an expected output line is treated as an empty line |
469 return '\n' # an expected output line is treated as an empty line |
|
470 |
440 |
471 |
441 class pyheredocmatcher(embeddedmatcher): |
472 class pyheredocmatcher(embeddedmatcher): |
442 """Detect "python << LIMIT" style embedded python code |
473 """Detect "python << LIMIT" style embedded python code |
443 |
474 |
444 >>> matcher = pyheredocmatcher() |
475 >>> matcher = pyheredocmatcher() |
496 |
529 |
497 def ignores(self, ctx): |
530 def ignores(self, ctx): |
498 return ' > %s\n' % heredocignorelimit == ctx |
531 return ' > %s\n' % heredocignorelimit == ctx |
499 |
532 |
500 def filename(self, ctx): |
533 def filename(self, ctx): |
501 return None # no filename |
534 return None # no filename |
502 |
535 |
503 def codeatstart(self, ctx, line): |
536 def codeatstart(self, ctx, line): |
504 return None # no embedded code at start line |
537 return None # no embedded code at start line |
505 |
538 |
506 def codeatend(self, ctx, line): |
539 def codeatend(self, ctx, line): |
507 return None # no embedded code at end line |
540 return None # no embedded code at end line |
508 |
541 |
509 def codeinside(self, ctx, line): |
542 def codeinside(self, ctx, line): |
510 return line[len(self._prefix):] # strip prefix |
543 return line[len(self._prefix) :] # strip prefix |
|
544 |
511 |
545 |
512 _pymatchers = [ |
546 _pymatchers = [ |
513 pydoctestmatcher(), |
547 pydoctestmatcher(), |
514 pyheredocmatcher(), |
548 pyheredocmatcher(), |
515 # use '[^<]+' instead of '\S+', in order to match against |
549 # use '[^<]+' instead of '\S+', in order to match against |
516 # paths including whitespaces |
550 # paths including whitespaces |
517 fileheredocmatcher('heredoc .py file', r'[^<]+\.py'), |
551 fileheredocmatcher('heredoc .py file', r'[^<]+\.py'), |
518 ] |
552 ] |
519 |
553 |
|
554 |
520 def pyembedded(basefile, lines, errors): |
555 def pyembedded(basefile, lines, errors): |
521 return embedded(basefile, lines, errors, _pymatchers) |
556 return embedded(basefile, lines, errors, _pymatchers) |
|
557 |
522 |
558 |
523 #### |
559 #### |
524 # for embedded shell script |
560 # for embedded shell script |
525 |
561 |
526 _shmatchers = [ |
562 _shmatchers = [ |
527 # use '[^<]+' instead of '\S+', in order to match against |
563 # use '[^<]+' instead of '\S+', in order to match against |
528 # paths including whitespaces |
564 # paths including whitespaces |
529 fileheredocmatcher('heredoc .sh file', r'[^<]+\.sh'), |
565 fileheredocmatcher('heredoc .sh file', r'[^<]+\.sh'), |
530 ] |
566 ] |
531 |
567 |
|
568 |
532 def shembedded(basefile, lines, errors): |
569 def shembedded(basefile, lines, errors): |
533 return embedded(basefile, lines, errors, _shmatchers) |
570 return embedded(basefile, lines, errors, _shmatchers) |
|
571 |
534 |
572 |
535 #### |
573 #### |
536 # for embedded hgrc configuration |
574 # for embedded hgrc configuration |
537 |
575 |
538 _hgrcmatchers = [ |
576 _hgrcmatchers = [ |
539 # use '[^<]+' instead of '\S+', in order to match against |
577 # use '[^<]+' instead of '\S+', in order to match against |
540 # paths including whitespaces |
578 # paths including whitespaces |
541 fileheredocmatcher('heredoc hgrc file', |
579 fileheredocmatcher( |
542 r'(([^/<]+/)+hgrc|\$HGRCPATH|\${HGRCPATH})'), |
580 'heredoc hgrc file', r'(([^/<]+/)+hgrc|\$HGRCPATH|\${HGRCPATH})' |
|
581 ), |
543 ] |
582 ] |
|
583 |
544 |
584 |
545 def hgrcembedded(basefile, lines, errors): |
585 def hgrcembedded(basefile, lines, errors): |
546 return embedded(basefile, lines, errors, _hgrcmatchers) |
586 return embedded(basefile, lines, errors, _hgrcmatchers) |
|
587 |
547 |
588 |
548 #### |
589 #### |
549 |
590 |
550 if __name__ == "__main__": |
591 if __name__ == "__main__": |
551 import optparse |
592 import optparse |
556 for name, starts, ends, code in embeddedfunc(basefile, lines, errors): |
597 for name, starts, ends, code in embeddedfunc(basefile, lines, errors): |
557 if not name: |
598 if not name: |
558 name = '<anonymous>' |
599 name = '<anonymous>' |
559 writeout("%s:%d: %s starts\n" % (basefile, starts, name)) |
600 writeout("%s:%d: %s starts\n" % (basefile, starts, name)) |
560 if opts.verbose and code: |
601 if opts.verbose and code: |
561 writeout(" |%s\n" % |
602 writeout(" |%s\n" % "\n |".join(l for l in code.splitlines())) |
562 "\n |".join(l for l in code.splitlines())) |
|
563 writeout("%s:%d: %s ends\n" % (basefile, ends, name)) |
603 writeout("%s:%d: %s ends\n" % (basefile, ends, name)) |
564 for e in errors: |
604 for e in errors: |
565 writeerr("%s\n" % e) |
605 writeerr("%s\n" % e) |
566 return len(errors) |
606 return len(errors) |
567 |
607 |
577 if showembedded('<stdin>', lines, embeddedfunc, opts): |
617 if showembedded('<stdin>', lines, embeddedfunc, opts): |
578 ret = 1 |
618 ret = 1 |
579 return ret |
619 return ret |
580 |
620 |
581 commands = {} |
621 commands = {} |
|
622 |
582 def command(name, desc): |
623 def command(name, desc): |
583 def wrap(func): |
624 def wrap(func): |
584 commands[name] = (desc, func) |
625 commands[name] = (desc, func) |
|
626 |
585 return wrap |
627 return wrap |
586 |
628 |
587 @command("pyembedded", "detect embedded python script") |
629 @command("pyembedded", "detect embedded python script") |
588 def pyembeddedcmd(args, opts): |
630 def pyembeddedcmd(args, opts): |
589 return applyembedded(args, pyembedded, opts) |
631 return applyembedded(args, pyembedded, opts) |
594 |
636 |
595 @command("hgrcembedded", "detect embedded hgrc configuration") |
637 @command("hgrcembedded", "detect embedded hgrc configuration") |
596 def hgrcembeddedcmd(args, opts): |
638 def hgrcembeddedcmd(args, opts): |
597 return applyembedded(args, hgrcembedded, opts) |
639 return applyembedded(args, hgrcembedded, opts) |
598 |
640 |
599 availablecommands = "\n".join([" - %s: %s" % (key, value[0]) |
641 availablecommands = "\n".join( |
600 for key, value in commands.items()]) |
642 [" - %s: %s" % (key, value[0]) for key, value in commands.items()] |
601 |
643 ) |
602 parser = optparse.OptionParser("""%prog COMMAND [file ...] |
644 |
|
645 parser = optparse.OptionParser( |
|
646 """%prog COMMAND [file ...] |
603 |
647 |
604 Pick up embedded code fragments from given file(s) or stdin, and list |
648 Pick up embedded code fragments from given file(s) or stdin, and list |
605 up start/end lines of them in standard compiler format |
649 up start/end lines of them in standard compiler format |
606 ("FILENAME:LINENO:"). |
650 ("FILENAME:LINENO:"). |
607 |
651 |
608 Available commands are: |
652 Available commands are: |
609 """ + availablecommands + """ |
653 """ |
610 """) |
654 + availablecommands |
611 parser.add_option("-v", "--verbose", |
655 + """ |
612 help="enable additional output (e.g. actual code)", |
656 """ |
613 action="store_true") |
657 ) |
|
658 parser.add_option( |
|
659 "-v", |
|
660 "--verbose", |
|
661 help="enable additional output (e.g. actual code)", |
|
662 action="store_true", |
|
663 ) |
614 (opts, args) = parser.parse_args() |
664 (opts, args) = parser.parse_args() |
615 |
665 |
616 if not args or args[0] not in commands: |
666 if not args or args[0] not in commands: |
617 parser.print_help() |
667 parser.print_help() |
618 sys.exit(255) |
668 sys.exit(255) |