python (3.11.7)

(root)/
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 ESC[4;38;5;81mHelpFormatter(ESC[4;38;5;149margparseESC[4;38;5;149m.ESC[4;38;5;149mHelpFormatter):
     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