]> jfr.im git - dlqueue.git/blob - venv/lib/python3.11/site-packages/pip/_vendor/pygments/cmdline.py
init: venv aand flask
[dlqueue.git] / venv / lib / python3.11 / site-packages / pip / _vendor / pygments / cmdline.py
1 """
2 pygments.cmdline
3 ~~~~~~~~~~~~~~~~
4
5 Command line interface.
6
7 :copyright: Copyright 2006-2023 by the Pygments team, see AUTHORS.
8 :license: BSD, see LICENSE for details.
9 """
10
11 import os
12 import sys
13 import shutil
14 import argparse
15 from textwrap import dedent
16
17 from pip._vendor.pygments import __version__, highlight
18 from pip._vendor.pygments.util import ClassNotFound, OptionError, docstring_headline, \
19 guess_decode, guess_decode_from_terminal, terminal_encoding, \
20 UnclosingTextIOWrapper
21 from pip._vendor.pygments.lexers import get_all_lexers, get_lexer_by_name, guess_lexer, \
22 load_lexer_from_file, get_lexer_for_filename, find_lexer_class_for_filename
23 from pip._vendor.pygments.lexers.special import TextLexer
24 from pip._vendor.pygments.formatters.latex import LatexEmbeddedLexer, LatexFormatter
25 from pip._vendor.pygments.formatters import get_all_formatters, get_formatter_by_name, \
26 load_formatter_from_file, get_formatter_for_filename, find_formatter_class
27 from pip._vendor.pygments.formatters.terminal import TerminalFormatter
28 from pip._vendor.pygments.formatters.terminal256 import Terminal256Formatter, TerminalTrueColorFormatter
29 from pip._vendor.pygments.filters import get_all_filters, find_filter_class
30 from pip._vendor.pygments.styles import get_all_styles, get_style_by_name
31
32
33 def _parse_options(o_strs):
34 opts = {}
35 if not o_strs:
36 return opts
37 for o_str in o_strs:
38 if not o_str.strip():
39 continue
40 o_args = o_str.split(',')
41 for o_arg in o_args:
42 o_arg = o_arg.strip()
43 try:
44 o_key, o_val = o_arg.split('=', 1)
45 o_key = o_key.strip()
46 o_val = o_val.strip()
47 except ValueError:
48 opts[o_arg] = True
49 else:
50 opts[o_key] = o_val
51 return opts
52
53
54 def _parse_filters(f_strs):
55 filters = []
56 if not f_strs:
57 return filters
58 for f_str in f_strs:
59 if ':' in f_str:
60 fname, fopts = f_str.split(':', 1)
61 filters.append((fname, _parse_options([fopts])))
62 else:
63 filters.append((f_str, {}))
64 return filters
65
66
67 def _print_help(what, name):
68 try:
69 if what == 'lexer':
70 cls = get_lexer_by_name(name)
71 print("Help on the %s lexer:" % cls.name)
72 print(dedent(cls.__doc__))
73 elif what == 'formatter':
74 cls = find_formatter_class(name)
75 print("Help on the %s formatter:" % cls.name)
76 print(dedent(cls.__doc__))
77 elif what == 'filter':
78 cls = find_filter_class(name)
79 print("Help on the %s filter:" % name)
80 print(dedent(cls.__doc__))
81 return 0
82 except (AttributeError, ValueError):
83 print("%s not found!" % what, file=sys.stderr)
84 return 1
85
86
87 def _print_list(what):
88 if what == 'lexer':
89 print()
90 print("Lexers:")
91 print("~~~~~~~")
92
93 info = []
94 for fullname, names, exts, _ in get_all_lexers():
95 tup = (', '.join(names)+':', fullname,
96 exts and '(filenames ' + ', '.join(exts) + ')' or '')
97 info.append(tup)
98 info.sort()
99 for i in info:
100 print(('* %s\n %s %s') % i)
101
102 elif what == 'formatter':
103 print()
104 print("Formatters:")
105 print("~~~~~~~~~~~")
106
107 info = []
108 for cls in get_all_formatters():
109 doc = docstring_headline(cls)
110 tup = (', '.join(cls.aliases) + ':', doc, cls.filenames and
111 '(filenames ' + ', '.join(cls.filenames) + ')' or '')
112 info.append(tup)
113 info.sort()
114 for i in info:
115 print(('* %s\n %s %s') % i)
116
117 elif what == 'filter':
118 print()
119 print("Filters:")
120 print("~~~~~~~~")
121
122 for name in get_all_filters():
123 cls = find_filter_class(name)
124 print("* " + name + ':')
125 print(" %s" % docstring_headline(cls))
126
127 elif what == 'style':
128 print()
129 print("Styles:")
130 print("~~~~~~~")
131
132 for name in get_all_styles():
133 cls = get_style_by_name(name)
134 print("* " + name + ':')
135 print(" %s" % docstring_headline(cls))
136
137
138 def _print_list_as_json(requested_items):
139 import json
140 result = {}
141 if 'lexer' in requested_items:
142 info = {}
143 for fullname, names, filenames, mimetypes in get_all_lexers():
144 info[fullname] = {
145 'aliases': names,
146 'filenames': filenames,
147 'mimetypes': mimetypes
148 }
149 result['lexers'] = info
150
151 if 'formatter' in requested_items:
152 info = {}
153 for cls in get_all_formatters():
154 doc = docstring_headline(cls)
155 info[cls.name] = {
156 'aliases': cls.aliases,
157 'filenames': cls.filenames,
158 'doc': doc
159 }
160 result['formatters'] = info
161
162 if 'filter' in requested_items:
163 info = {}
164 for name in get_all_filters():
165 cls = find_filter_class(name)
166 info[name] = {
167 'doc': docstring_headline(cls)
168 }
169 result['filters'] = info
170
171 if 'style' in requested_items:
172 info = {}
173 for name in get_all_styles():
174 cls = get_style_by_name(name)
175 info[name] = {
176 'doc': docstring_headline(cls)
177 }
178 result['styles'] = info
179
180 json.dump(result, sys.stdout)
181
182 def main_inner(parser, argns):
183 if argns.help:
184 parser.print_help()
185 return 0
186
187 if argns.V:
188 print('Pygments version %s, (c) 2006-2023 by Georg Brandl, Matthäus '
189 'Chajdas and contributors.' % __version__)
190 return 0
191
192 def is_only_option(opt):
193 return not any(v for (k, v) in vars(argns).items() if k != opt)
194
195 # handle ``pygmentize -L``
196 if argns.L is not None:
197 arg_set = set()
198 for k, v in vars(argns).items():
199 if v:
200 arg_set.add(k)
201
202 arg_set.discard('L')
203 arg_set.discard('json')
204
205 if arg_set:
206 parser.print_help(sys.stderr)
207 return 2
208
209 # print version
210 if not argns.json:
211 main(['', '-V'])
212 allowed_types = {'lexer', 'formatter', 'filter', 'style'}
213 largs = [arg.rstrip('s') for arg in argns.L]
214 if any(arg not in allowed_types for arg in largs):
215 parser.print_help(sys.stderr)
216 return 0
217 if not largs:
218 largs = allowed_types
219 if not argns.json:
220 for arg in largs:
221 _print_list(arg)
222 else:
223 _print_list_as_json(largs)
224 return 0
225
226 # handle ``pygmentize -H``
227 if argns.H:
228 if not is_only_option('H'):
229 parser.print_help(sys.stderr)
230 return 2
231 what, name = argns.H
232 if what not in ('lexer', 'formatter', 'filter'):
233 parser.print_help(sys.stderr)
234 return 2
235 return _print_help(what, name)
236
237 # parse -O options
238 parsed_opts = _parse_options(argns.O or [])
239
240 # parse -P options
241 for p_opt in argns.P or []:
242 try:
243 name, value = p_opt.split('=', 1)
244 except ValueError:
245 parsed_opts[p_opt] = True
246 else:
247 parsed_opts[name] = value
248
249 # encodings
250 inencoding = parsed_opts.get('inencoding', parsed_opts.get('encoding'))
251 outencoding = parsed_opts.get('outencoding', parsed_opts.get('encoding'))
252
253 # handle ``pygmentize -N``
254 if argns.N:
255 lexer = find_lexer_class_for_filename(argns.N)
256 if lexer is None:
257 lexer = TextLexer
258
259 print(lexer.aliases[0])
260 return 0
261
262 # handle ``pygmentize -C``
263 if argns.C:
264 inp = sys.stdin.buffer.read()
265 try:
266 lexer = guess_lexer(inp, inencoding=inencoding)
267 except ClassNotFound:
268 lexer = TextLexer
269
270 print(lexer.aliases[0])
271 return 0
272
273 # handle ``pygmentize -S``
274 S_opt = argns.S
275 a_opt = argns.a
276 if S_opt is not None:
277 f_opt = argns.f
278 if not f_opt:
279 parser.print_help(sys.stderr)
280 return 2
281 if argns.l or argns.INPUTFILE:
282 parser.print_help(sys.stderr)
283 return 2
284
285 try:
286 parsed_opts['style'] = S_opt
287 fmter = get_formatter_by_name(f_opt, **parsed_opts)
288 except ClassNotFound as err:
289 print(err, file=sys.stderr)
290 return 1
291
292 print(fmter.get_style_defs(a_opt or ''))
293 return 0
294
295 # if no -S is given, -a is not allowed
296 if argns.a is not None:
297 parser.print_help(sys.stderr)
298 return 2
299
300 # parse -F options
301 F_opts = _parse_filters(argns.F or [])
302
303 # -x: allow custom (eXternal) lexers and formatters
304 allow_custom_lexer_formatter = bool(argns.x)
305
306 # select lexer
307 lexer = None
308
309 # given by name?
310 lexername = argns.l
311 if lexername:
312 # custom lexer, located relative to user's cwd
313 if allow_custom_lexer_formatter and '.py' in lexername:
314 try:
315 filename = None
316 name = None
317 if ':' in lexername:
318 filename, name = lexername.rsplit(':', 1)
319
320 if '.py' in name:
321 # This can happen on Windows: If the lexername is
322 # C:\lexer.py -- return to normal load path in that case
323 name = None
324
325 if filename and name:
326 lexer = load_lexer_from_file(filename, name,
327 **parsed_opts)
328 else:
329 lexer = load_lexer_from_file(lexername, **parsed_opts)
330 except ClassNotFound as err:
331 print('Error:', err, file=sys.stderr)
332 return 1
333 else:
334 try:
335 lexer = get_lexer_by_name(lexername, **parsed_opts)
336 except (OptionError, ClassNotFound) as err:
337 print('Error:', err, file=sys.stderr)
338 return 1
339
340 # read input code
341 code = None
342
343 if argns.INPUTFILE:
344 if argns.s:
345 print('Error: -s option not usable when input file specified',
346 file=sys.stderr)
347 return 2
348
349 infn = argns.INPUTFILE
350 try:
351 with open(infn, 'rb') as infp:
352 code = infp.read()
353 except Exception as err:
354 print('Error: cannot read infile:', err, file=sys.stderr)
355 return 1
356 if not inencoding:
357 code, inencoding = guess_decode(code)
358
359 # do we have to guess the lexer?
360 if not lexer:
361 try:
362 lexer = get_lexer_for_filename(infn, code, **parsed_opts)
363 except ClassNotFound as err:
364 if argns.g:
365 try:
366 lexer = guess_lexer(code, **parsed_opts)
367 except ClassNotFound:
368 lexer = TextLexer(**parsed_opts)
369 else:
370 print('Error:', err, file=sys.stderr)
371 return 1
372 except OptionError as err:
373 print('Error:', err, file=sys.stderr)
374 return 1
375
376 elif not argns.s: # treat stdin as full file (-s support is later)
377 # read code from terminal, always in binary mode since we want to
378 # decode ourselves and be tolerant with it
379 code = sys.stdin.buffer.read() # use .buffer to get a binary stream
380 if not inencoding:
381 code, inencoding = guess_decode_from_terminal(code, sys.stdin)
382 # else the lexer will do the decoding
383 if not lexer:
384 try:
385 lexer = guess_lexer(code, **parsed_opts)
386 except ClassNotFound:
387 lexer = TextLexer(**parsed_opts)
388
389 else: # -s option needs a lexer with -l
390 if not lexer:
391 print('Error: when using -s a lexer has to be selected with -l',
392 file=sys.stderr)
393 return 2
394
395 # process filters
396 for fname, fopts in F_opts:
397 try:
398 lexer.add_filter(fname, **fopts)
399 except ClassNotFound as err:
400 print('Error:', err, file=sys.stderr)
401 return 1
402
403 # select formatter
404 outfn = argns.o
405 fmter = argns.f
406 if fmter:
407 # custom formatter, located relative to user's cwd
408 if allow_custom_lexer_formatter and '.py' in fmter:
409 try:
410 filename = None
411 name = None
412 if ':' in fmter:
413 # Same logic as above for custom lexer
414 filename, name = fmter.rsplit(':', 1)
415
416 if '.py' in name:
417 name = None
418
419 if filename and name:
420 fmter = load_formatter_from_file(filename, name,
421 **parsed_opts)
422 else:
423 fmter = load_formatter_from_file(fmter, **parsed_opts)
424 except ClassNotFound as err:
425 print('Error:', err, file=sys.stderr)
426 return 1
427 else:
428 try:
429 fmter = get_formatter_by_name(fmter, **parsed_opts)
430 except (OptionError, ClassNotFound) as err:
431 print('Error:', err, file=sys.stderr)
432 return 1
433
434 if outfn:
435 if not fmter:
436 try:
437 fmter = get_formatter_for_filename(outfn, **parsed_opts)
438 except (OptionError, ClassNotFound) as err:
439 print('Error:', err, file=sys.stderr)
440 return 1
441 try:
442 outfile = open(outfn, 'wb')
443 except Exception as err:
444 print('Error: cannot open outfile:', err, file=sys.stderr)
445 return 1
446 else:
447 if not fmter:
448 if os.environ.get('COLORTERM','') in ('truecolor', '24bit'):
449 fmter = TerminalTrueColorFormatter(**parsed_opts)
450 elif '256' in os.environ.get('TERM', ''):
451 fmter = Terminal256Formatter(**parsed_opts)
452 else:
453 fmter = TerminalFormatter(**parsed_opts)
454 outfile = sys.stdout.buffer
455
456 # determine output encoding if not explicitly selected
457 if not outencoding:
458 if outfn:
459 # output file? use lexer encoding for now (can still be None)
460 fmter.encoding = inencoding
461 else:
462 # else use terminal encoding
463 fmter.encoding = terminal_encoding(sys.stdout)
464
465 # provide coloring under Windows, if possible
466 if not outfn and sys.platform in ('win32', 'cygwin') and \
467 fmter.name in ('Terminal', 'Terminal256'): # pragma: no cover
468 # unfortunately colorama doesn't support binary streams on Py3
469 outfile = UnclosingTextIOWrapper(outfile, encoding=fmter.encoding)
470 fmter.encoding = None
471 try:
472 import pip._vendor.colorama.initialise as colorama_initialise
473 except ImportError:
474 pass
475 else:
476 outfile = colorama_initialise.wrap_stream(
477 outfile, convert=None, strip=None, autoreset=False, wrap=True)
478
479 # When using the LaTeX formatter and the option `escapeinside` is
480 # specified, we need a special lexer which collects escaped text
481 # before running the chosen language lexer.
482 escapeinside = parsed_opts.get('escapeinside', '')
483 if len(escapeinside) == 2 and isinstance(fmter, LatexFormatter):
484 left = escapeinside[0]
485 right = escapeinside[1]
486 lexer = LatexEmbeddedLexer(left, right, lexer)
487
488 # ... and do it!
489 if not argns.s:
490 # process whole input as per normal...
491 try:
492 highlight(code, lexer, fmter, outfile)
493 finally:
494 if outfn:
495 outfile.close()
496 return 0
497 else:
498 # line by line processing of stdin (eg: for 'tail -f')...
499 try:
500 while 1:
501 line = sys.stdin.buffer.readline()
502 if not line:
503 break
504 if not inencoding:
505 line = guess_decode_from_terminal(line, sys.stdin)[0]
506 highlight(line, lexer, fmter, outfile)
507 if hasattr(outfile, 'flush'):
508 outfile.flush()
509 return 0
510 except KeyboardInterrupt: # pragma: no cover
511 return 0
512 finally:
513 if outfn:
514 outfile.close()
515
516
517 class HelpFormatter(argparse.HelpFormatter):
518 def __init__(self, prog, indent_increment=2, max_help_position=16, width=None):
519 if width is None:
520 try:
521 width = shutil.get_terminal_size().columns - 2
522 except Exception:
523 pass
524 argparse.HelpFormatter.__init__(self, prog, indent_increment,
525 max_help_position, width)
526
527
528 def main(args=sys.argv):
529 """
530 Main command line entry point.
531 """
532 desc = "Highlight an input file and write the result to an output file."
533 parser = argparse.ArgumentParser(description=desc, add_help=False,
534 formatter_class=HelpFormatter)
535
536 operation = parser.add_argument_group('Main operation')
537 lexersel = operation.add_mutually_exclusive_group()
538 lexersel.add_argument(
539 '-l', metavar='LEXER',
540 help='Specify the lexer to use. (Query names with -L.) If not '
541 'given and -g is not present, the lexer is guessed from the filename.')
542 lexersel.add_argument(
543 '-g', action='store_true',
544 help='Guess the lexer from the file contents, or pass through '
545 'as plain text if nothing can be guessed.')
546 operation.add_argument(
547 '-F', metavar='FILTER[:options]', action='append',
548 help='Add a filter to the token stream. (Query names with -L.) '
549 'Filter options are given after a colon if necessary.')
550 operation.add_argument(
551 '-f', metavar='FORMATTER',
552 help='Specify the formatter to use. (Query names with -L.) '
553 'If not given, the formatter is guessed from the output filename, '
554 'and defaults to the terminal formatter if the output is to the '
555 'terminal or an unknown file extension.')
556 operation.add_argument(
557 '-O', metavar='OPTION=value[,OPTION=value,...]', action='append',
558 help='Give options to the lexer and formatter as a comma-separated '
559 'list of key-value pairs. '
560 'Example: `-O bg=light,python=cool`.')
561 operation.add_argument(
562 '-P', metavar='OPTION=value', action='append',
563 help='Give a single option to the lexer and formatter - with this '
564 'you can pass options whose value contains commas and equal signs. '
565 'Example: `-P "heading=Pygments, the Python highlighter"`.')
566 operation.add_argument(
567 '-o', metavar='OUTPUTFILE',
568 help='Where to write the output. Defaults to standard output.')
569
570 operation.add_argument(
571 'INPUTFILE', nargs='?',
572 help='Where to read the input. Defaults to standard input.')
573
574 flags = parser.add_argument_group('Operation flags')
575 flags.add_argument(
576 '-v', action='store_true',
577 help='Print a detailed traceback on unhandled exceptions, which '
578 'is useful for debugging and bug reports.')
579 flags.add_argument(
580 '-s', action='store_true',
581 help='Process lines one at a time until EOF, rather than waiting to '
582 'process the entire file. This only works for stdin, only for lexers '
583 'with no line-spanning constructs, and is intended for streaming '
584 'input such as you get from `tail -f`. '
585 'Example usage: `tail -f sql.log | pygmentize -s -l sql`.')
586 flags.add_argument(
587 '-x', action='store_true',
588 help='Allow custom lexers and formatters to be loaded from a .py file '
589 'relative to the current working directory. For example, '
590 '`-l ./customlexer.py -x`. By default, this option expects a file '
591 'with a class named CustomLexer or CustomFormatter; you can also '
592 'specify your own class name with a colon (`-l ./lexer.py:MyLexer`). '
593 'Users should be very careful not to use this option with untrusted '
594 'files, because it will import and run them.')
595 flags.add_argument('--json', help='Output as JSON. This can '
596 'be only used in conjunction with -L.',
597 default=False,
598 action='store_true')
599
600 special_modes_group = parser.add_argument_group(
601 'Special modes - do not do any highlighting')
602 special_modes = special_modes_group.add_mutually_exclusive_group()
603 special_modes.add_argument(
604 '-S', metavar='STYLE -f formatter',
605 help='Print style definitions for STYLE for a formatter '
606 'given with -f. The argument given by -a is formatter '
607 'dependent.')
608 special_modes.add_argument(
609 '-L', nargs='*', metavar='WHAT',
610 help='List lexers, formatters, styles or filters -- '
611 'give additional arguments for the thing(s) you want to list '
612 '(e.g. "styles"), or omit them to list everything.')
613 special_modes.add_argument(
614 '-N', metavar='FILENAME',
615 help='Guess and print out a lexer name based solely on the given '
616 'filename. Does not take input or highlight anything. If no specific '
617 'lexer can be determined, "text" is printed.')
618 special_modes.add_argument(
619 '-C', action='store_true',
620 help='Like -N, but print out a lexer name based solely on '
621 'a given content from standard input.')
622 special_modes.add_argument(
623 '-H', action='store', nargs=2, metavar=('NAME', 'TYPE'),
624 help='Print detailed help for the object <name> of type <type>, '
625 'where <type> is one of "lexer", "formatter" or "filter".')
626 special_modes.add_argument(
627 '-V', action='store_true',
628 help='Print the package version.')
629 special_modes.add_argument(
630 '-h', '--help', action='store_true',
631 help='Print this help.')
632 special_modes_group.add_argument(
633 '-a', metavar='ARG',
634 help='Formatter-specific additional argument for the -S (print '
635 'style sheet) mode.')
636
637 argns = parser.parse_args(args[1:])
638
639 try:
640 return main_inner(parser, argns)
641 except BrokenPipeError:
642 # someone closed our stdout, e.g. by quitting a pager.
643 return 0
644 except Exception:
645 if argns.v:
646 print(file=sys.stderr)
647 print('*' * 65, file=sys.stderr)
648 print('An unhandled exception occurred while highlighting.',
649 file=sys.stderr)
650 print('Please report the whole traceback to the issue tracker at',
651 file=sys.stderr)
652 print('<https://github.com/pygments/pygments/issues>.',
653 file=sys.stderr)
654 print('*' * 65, file=sys.stderr)
655 print(file=sys.stderr)
656 raise
657 import traceback
658 info = traceback.format_exception(*sys.exc_info())
659 msg = info[-1].strip()
660 if len(info) >= 3:
661 # extract relevant file and position info
662 msg += '\n (f%s)' % info[-2].split('\n')[0].strip()[1:]
663 print(file=sys.stderr)
664 print('*** Error while highlighting:', file=sys.stderr)
665 print(msg, file=sys.stderr)
666 print('*** If this is a bug you want to report, please rerun with -v.',
667 file=sys.stderr)
668 return 1