17 |
17 |
18 from pygments import __version__, highlight |
18 from pygments import __version__, highlight |
19 from pygments.util import ClassNotFound, OptionError, docstring_headline, \ |
19 from pygments.util import ClassNotFound, OptionError, docstring_headline, \ |
20 guess_decode, guess_decode_from_terminal, terminal_encoding |
20 guess_decode, guess_decode_from_terminal, terminal_encoding |
21 from pygments.lexers import get_all_lexers, get_lexer_by_name, guess_lexer, \ |
21 from pygments.lexers import get_all_lexers, get_lexer_by_name, guess_lexer, \ |
22 get_lexer_for_filename, find_lexer_class, TextLexer |
22 get_lexer_for_filename, find_lexer_class_for_filename |
|
23 from pygments.lexers.special import TextLexer |
23 from pygments.formatters.latex import LatexEmbeddedLexer, LatexFormatter |
24 from pygments.formatters.latex import LatexEmbeddedLexer, LatexFormatter |
24 from pygments.formatters import get_all_formatters, get_formatter_by_name, \ |
25 from pygments.formatters import get_all_formatters, get_formatter_by_name, \ |
25 get_formatter_for_filename, find_formatter_class, \ |
26 get_formatter_for_filename, find_formatter_class |
26 TerminalFormatter # pylint:disable-msg=E0611 |
27 from pygments.formatters.terminal import TerminalFormatter |
27 from pygments.filters import get_all_filters, find_filter_class |
28 from pygments.filters import get_all_filters, find_filter_class |
28 from pygments.styles import get_all_styles, get_style_by_name |
29 from pygments.styles import get_all_styles, get_style_by_name |
29 |
30 |
30 |
31 |
31 USAGE = """\ |
32 USAGE = """\ |
32 Usage: %s [-l <lexer> | -g] [-F <filter>[:<options>]] [-f <formatter>] |
33 Usage: %s [-l <lexer> | -g] [-F <filter>[:<options>]] [-f <formatter>] |
33 [-O <options>] [-P <option=value>] [-s] [-o <outfile>] [<infile>] |
34 [-O <options>] [-P <option=value>] [-s] [-v] [-o <outfile>] [<infile>] |
34 |
35 |
35 %s -S <style> -f <formatter> [-a <arg>] [-O <options>] [-P <option=value>] |
36 %s -S <style> -f <formatter> [-a <arg>] [-O <options>] [-P <option=value>] |
36 %s -L [<which> ...] |
37 %s -L [<which> ...] |
37 %s -N <filename> |
38 %s -N <filename> |
38 %s -H <type> <name> |
39 %s -H <type> <name> |
88 The -s option processes lines one at a time until EOF, rather than |
89 The -s option processes lines one at a time until EOF, rather than |
89 waiting to process the entire file. This only works for stdin, and |
90 waiting to process the entire file. This only works for stdin, and |
90 is intended for streaming input such as you get from 'tail -f'. |
91 is intended for streaming input such as you get from 'tail -f'. |
91 Example usage: "tail -f sql.log | pygmentize -s -l sql" |
92 Example usage: "tail -f sql.log | pygmentize -s -l sql" |
92 |
93 |
|
94 The -v option prints a detailed traceback on unhandled exceptions, |
|
95 which is useful for debugging and bug reports. |
|
96 |
93 The -h option prints this help. |
97 The -h option prints this help. |
94 The -V option prints the package version. |
98 The -V option prints the package version. |
95 """ |
99 """ |
96 |
100 |
97 |
101 |
98 def _parse_options(o_strs): |
102 def _parse_options(o_strs): |
99 opts = {} |
103 opts = {} |
100 if not o_strs: |
104 if not o_strs: |
101 return opts |
105 return opts |
102 for o_str in o_strs: |
106 for o_str in o_strs: |
103 if not o_str: |
107 if not o_str.strip(): |
104 continue |
108 continue |
105 o_args = o_str.split(',') |
109 o_args = o_str.split(',') |
106 for o_arg in o_args: |
110 for o_arg in o_args: |
107 o_arg = o_arg.strip() |
111 o_arg = o_arg.strip() |
108 try: |
112 try: |
130 |
134 |
131 |
135 |
132 def _print_help(what, name): |
136 def _print_help(what, name): |
133 try: |
137 try: |
134 if what == 'lexer': |
138 if what == 'lexer': |
135 cls = find_lexer_class(name) |
139 cls = get_lexer_by_name(name) |
136 print("Help on the %s lexer:" % cls.name) |
140 print("Help on the %s lexer:" % cls.name) |
137 print(dedent(cls.__doc__)) |
141 print(dedent(cls.__doc__)) |
138 elif what == 'formatter': |
142 elif what == 'formatter': |
139 cls = find_formatter_class(name) |
143 cls = find_formatter_class(name) |
140 print("Help on the %s formatter:" % cls.name) |
144 print("Help on the %s formatter:" % cls.name) |
141 print(dedent(cls.__doc__)) |
145 print(dedent(cls.__doc__)) |
142 elif what == 'filter': |
146 elif what == 'filter': |
143 cls = find_filter_class(name) |
147 cls = find_filter_class(name) |
144 print("Help on the %s filter:" % name) |
148 print("Help on the %s filter:" % name) |
145 print(dedent(cls.__doc__)) |
149 print(dedent(cls.__doc__)) |
146 except AttributeError: |
150 return 0 |
|
151 except (AttributeError, ValueError): |
147 print("%s not found!" % what, file=sys.stderr) |
152 print("%s not found!" % what, file=sys.stderr) |
|
153 return 1 |
148 |
154 |
149 |
155 |
150 def _print_list(what): |
156 def _print_list(what): |
151 if what == 'lexer': |
157 if what == 'lexer': |
152 print() |
158 print() |
215 if opts.pop('-h', None) is not None: |
221 if opts.pop('-h', None) is not None: |
216 print(usage) |
222 print(usage) |
217 return 0 |
223 return 0 |
218 |
224 |
219 if opts.pop('-V', None) is not None: |
225 if opts.pop('-V', None) is not None: |
220 print('Pygments version %s, (c) 2006-2014 by Georg Brandl.' % __version__) |
226 print('Pygments version %s, (c) 2006-2015 by Georg Brandl.' % __version__) |
221 return 0 |
227 return 0 |
222 |
228 |
223 # handle ``pygmentize -L`` |
229 # handle ``pygmentize -L`` |
224 L_opt = opts.pop('-L', None) |
230 L_opt = opts.pop('-L', None) |
225 if L_opt is not None: |
231 if L_opt is not None: |
240 if H_opt is not None: |
246 if H_opt is not None: |
241 if opts or len(args) != 2: |
247 if opts or len(args) != 2: |
242 print(usage, file=sys.stderr) |
248 print(usage, file=sys.stderr) |
243 return 2 |
249 return 2 |
244 |
250 |
245 what, name = args |
251 what, name = args # pylint: disable=unbalanced-tuple-unpacking |
246 if what not in ('lexer', 'formatter', 'filter'): |
252 if what not in ('lexer', 'formatter', 'filter'): |
247 print(usage, file=sys.stderr) |
253 print(usage, file=sys.stderr) |
248 return 2 |
254 return 2 |
249 |
255 |
250 _print_help(what, name) |
256 return _print_help(what, name) |
251 return 0 |
|
252 |
257 |
253 # parse -O options |
258 # parse -O options |
254 parsed_opts = _parse_options(O_opts) |
259 parsed_opts = _parse_options(O_opts) |
255 opts.pop('-O', None) |
260 opts.pop('-O', None) |
256 |
261 |
263 else: |
268 else: |
264 parsed_opts[name] = value |
269 parsed_opts[name] = value |
265 opts.pop('-P', None) |
270 opts.pop('-P', None) |
266 |
271 |
267 # encodings |
272 # encodings |
268 inencoding = parsed_opts.get('inencoding', parsed_opts.get('encoding')) |
273 inencoding = parsed_opts.get('inencoding', parsed_opts.get('encoding')) |
269 outencoding = parsed_opts.get('outencoding', parsed_opts.get('encoding')) |
274 outencoding = parsed_opts.get('outencoding', parsed_opts.get('encoding')) |
270 |
275 |
271 # handle ``pygmentize -N`` |
276 # handle ``pygmentize -N`` |
272 infn = opts.pop('-N', None) |
277 infn = opts.pop('-N', None) |
273 if infn is not None: |
278 if infn is not None: |
274 try: |
279 lexer = find_lexer_class_for_filename(infn) |
275 lexer = get_lexer_for_filename(infn, **parsed_opts) |
280 if lexer is None: |
276 except ClassNotFound as err: |
281 lexer = TextLexer |
277 lexer = TextLexer() |
|
278 except OptionError as err: |
|
279 print('Error:', err, file=sys.stderr) |
|
280 return 1 |
|
281 |
282 |
282 print(lexer.aliases[0]) |
283 print(lexer.aliases[0]) |
283 return 0 |
284 return 0 |
284 |
285 |
285 # handle ``pygmentize -S`` |
286 # handle ``pygmentize -S`` |
299 fmter = get_formatter_by_name(f_opt, **parsed_opts) |
300 fmter = get_formatter_by_name(f_opt, **parsed_opts) |
300 except ClassNotFound as err: |
301 except ClassNotFound as err: |
301 print(err, file=sys.stderr) |
302 print(err, file=sys.stderr) |
302 return 1 |
303 return 1 |
303 |
304 |
304 arg = a_opt or '' |
305 print(fmter.get_style_defs(a_opt or '')) |
305 try: |
|
306 print(fmter.get_style_defs(arg)) |
|
307 except Exception as err: |
|
308 print('Error:', err, file=sys.stderr) |
|
309 return 1 |
|
310 return 0 |
306 return 0 |
311 |
307 |
312 # if no -S is given, -a is not allowed |
308 # if no -S is given, -a is not allowed |
313 if a_opt is not None: |
309 if a_opt is not None: |
314 print(usage, file=sys.stderr) |
310 print(usage, file=sys.stderr) |
385 try: |
381 try: |
386 lexer = guess_lexer(code, **parsed_opts) |
382 lexer = guess_lexer(code, **parsed_opts) |
387 except ClassNotFound: |
383 except ClassNotFound: |
388 lexer = TextLexer(**parsed_opts) |
384 lexer = TextLexer(**parsed_opts) |
389 |
385 |
|
386 else: # -s option needs a lexer with -l |
|
387 if not lexer: |
|
388 print('Error: when using -s a lexer has to be selected with -l', |
|
389 file=sys.stderr) |
|
390 return 2 |
|
391 |
|
392 # process filters |
|
393 for fname, fopts in F_opts: |
|
394 try: |
|
395 lexer.add_filter(fname, **fopts) |
|
396 except ClassNotFound as err: |
|
397 print('Error:', err, file=sys.stderr) |
|
398 return 1 |
|
399 |
390 # select formatter |
400 # select formatter |
391 outfn = opts.pop('-o', None) |
401 outfn = opts.pop('-o', None) |
392 fmter = opts.pop('-f', None) |
402 fmter = opts.pop('-f', None) |
393 if fmter: |
403 if fmter: |
394 try: |
404 try: |
427 # else use terminal encoding |
437 # else use terminal encoding |
428 fmter.encoding = terminal_encoding(sys.stdout) |
438 fmter.encoding = terminal_encoding(sys.stdout) |
429 |
439 |
430 # provide coloring under Windows, if possible |
440 # provide coloring under Windows, if possible |
431 if not outfn and sys.platform in ('win32', 'cygwin') and \ |
441 if not outfn and sys.platform in ('win32', 'cygwin') and \ |
432 fmter.name in ('Terminal', 'Terminal256'): |
442 fmter.name in ('Terminal', 'Terminal256'): # pragma: no cover |
433 # unfortunately colorama doesn't support binary streams on Py3 |
443 # unfortunately colorama doesn't support binary streams on Py3 |
434 if sys.version_info > (3,): |
444 if sys.version_info > (3,): |
435 from pygments.util import UnclosingTextIOWrapper |
445 from pygments.util import UnclosingTextIOWrapper |
436 outfile = UnclosingTextIOWrapper(outfile, encoding=fmter.encoding) |
446 outfile = UnclosingTextIOWrapper(outfile, encoding=fmter.encoding) |
437 fmter.encoding = None |
447 fmter.encoding = None |
450 if len(escapeinside) == 2 and isinstance(fmter, LatexFormatter): |
460 if len(escapeinside) == 2 and isinstance(fmter, LatexFormatter): |
451 left = escapeinside[0] |
461 left = escapeinside[0] |
452 right = escapeinside[1] |
462 right = escapeinside[1] |
453 lexer = LatexEmbeddedLexer(left, right, lexer) |
463 lexer = LatexEmbeddedLexer(left, right, lexer) |
454 |
464 |
455 # process filters |
|
456 for fname, fopts in F_opts: |
|
457 try: |
|
458 lexer.add_filter(fname, **fopts) |
|
459 except ClassNotFound as err: |
|
460 print('Error:', err, file=sys.stderr) |
|
461 return 1 |
|
462 |
|
463 # ... and do it! |
465 # ... and do it! |
464 if '-s' not in opts: |
466 if '-s' not in opts: |
465 # process whole input as per normal... |
467 # process whole input as per normal... |
466 highlight(code, lexer, fmter, outfile) |
468 highlight(code, lexer, fmter, outfile) |
467 return 0 |
469 return 0 |
468 else: |
470 else: |
469 if not lexer: |
|
470 print('Error: when using -s a lexer has to be selected with -l', |
|
471 file=sys.stderr) |
|
472 return 1 |
|
473 # line by line processing of stdin (eg: for 'tail -f')... |
471 # line by line processing of stdin (eg: for 'tail -f')... |
474 try: |
472 try: |
475 while 1: |
473 while 1: |
476 if sys.version_info > (3,): |
474 if sys.version_info > (3,): |
477 # Python 3: we have to use .buffer to get a binary stream |
475 # Python 3: we have to use .buffer to get a binary stream |
483 if not inencoding: |
481 if not inencoding: |
484 line = guess_decode_from_terminal(line, sys.stdin)[0] |
482 line = guess_decode_from_terminal(line, sys.stdin)[0] |
485 highlight(line, lexer, fmter, outfile) |
483 highlight(line, lexer, fmter, outfile) |
486 if hasattr(outfile, 'flush'): |
484 if hasattr(outfile, 'flush'): |
487 outfile.flush() |
485 outfile.flush() |
488 except KeyboardInterrupt: |
486 return 0 |
|
487 except KeyboardInterrupt: # pragma: no cover |
489 return 0 |
488 return 0 |
490 |
489 |
491 |
490 |
492 def main(args=sys.argv): |
491 def main(args=sys.argv): |
493 """ |
492 """ |
494 Main command line entry point. |
493 Main command line entry point. |
495 """ |
494 """ |
496 usage = USAGE % ((args[0],) * 6) |
495 usage = USAGE % ((args[0],) * 6) |
497 |
496 |
498 try: |
497 try: |
499 popts, args = getopt.getopt(args[1:], "l:f:F:o:O:P:LS:a:N:hVHgs") |
498 popts, args = getopt.getopt(args[1:], "l:f:F:o:O:P:LS:a:N:vhVHgs") |
500 except getopt.GetoptError: |
499 except getopt.GetoptError: |
501 print(usage, file=sys.stderr) |
500 print(usage, file=sys.stderr) |
502 return 2 |
501 return 2 |
503 |
502 |
504 try: |
503 try: |
505 return main_inner(popts, args, usage) |
504 return main_inner(popts, args, usage) |
506 except Exception: |
505 except Exception: |
|
506 if '-v' in dict(popts): |
|
507 print(file=sys.stderr) |
|
508 print('*' * 65, file=sys.stderr) |
|
509 print('An unhandled exception occurred while highlighting.', |
|
510 file=sys.stderr) |
|
511 print('Please report the whole traceback to the issue tracker at', |
|
512 file=sys.stderr) |
|
513 print('<https://bitbucket.org/birkenfeld/pygments-main/issues>.', |
|
514 file=sys.stderr) |
|
515 print('*' * 65, file=sys.stderr) |
|
516 print(file=sys.stderr) |
|
517 raise |
507 import traceback |
518 import traceback |
508 info = traceback.format_exception(*sys.exc_info()) |
519 info = traceback.format_exception(*sys.exc_info()) |
509 msg = info[-1].strip() |
520 msg = info[-1].strip() |
510 if len(info) >= 3: |
521 if len(info) >= 3: |
511 # extract relevant file and position info |
522 # extract relevant file and position info |
512 msg += '\n (f%s)' % info[-2].split('\n')[0].strip()[1:] |
523 msg += '\n (f%s)' % info[-2].split('\n')[0].strip()[1:] |
513 print(file=sys.stderr) |
524 print(file=sys.stderr) |
514 print('*** Error while highlighting:', file=sys.stderr) |
525 print('*** Error while highlighting:', file=sys.stderr) |
515 print(msg, file=sys.stderr) |
526 print(msg, file=sys.stderr) |
|
527 print('*** If this is a bug you want to report, please rerun with -v.', |
|
528 file=sys.stderr) |
516 return 1 |
529 return 1 |