rana-cli/wakatime/packages/pygments/cmdline.py

574 lines
19 KiB
Python
Raw Normal View History

# -*- coding: utf-8 -*-
"""
pygments.cmdline
~~~~~~~~~~~~~~~~
Command line interface.
2017-02-14 07:25:51 +00:00
:copyright: Copyright 2006-2017 by the Pygments team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
2014-12-01 06:10:30 +00:00
from __future__ import print_function
2017-02-14 07:25:51 +00:00
import os
import sys
import getopt
from textwrap import dedent
from pygments import __version__, highlight
2014-12-01 06:10:30 +00:00
from pygments.util import ClassNotFound, OptionError, docstring_headline, \
guess_decode, guess_decode_from_terminal, terminal_encoding
from pygments.lexers import get_all_lexers, get_lexer_by_name, guess_lexer, \
2017-02-14 07:25:51 +00:00
load_lexer_from_file, get_lexer_for_filename, find_lexer_class_for_filename
2016-06-13 14:41:17 +00:00
from pygments.lexers.special import TextLexer
2014-12-01 06:10:30 +00:00
from pygments.formatters.latex import LatexEmbeddedLexer, LatexFormatter
from pygments.formatters import get_all_formatters, get_formatter_by_name, \
2017-02-14 07:25:51 +00:00
load_formatter_from_file, get_formatter_for_filename, find_formatter_class
2016-06-13 14:41:17 +00:00
from pygments.formatters.terminal import TerminalFormatter
2017-02-14 07:25:51 +00:00
from pygments.formatters.terminal256 import Terminal256Formatter
from pygments.filters import get_all_filters, find_filter_class
from pygments.styles import get_all_styles, get_style_by_name
USAGE = """\
Usage: %s [-l <lexer> | -g] [-F <filter>[:<options>]] [-f <formatter>]
2017-02-14 07:25:51 +00:00
[-O <options>] [-P <option=value>] [-s] [-v] [-x] [-o <outfile>] [<infile>]
%s -S <style> -f <formatter> [-a <arg>] [-O <options>] [-P <option=value>]
%s -L [<which> ...]
%s -N <filename>
%s -H <type> <name>
%s -h | -V
Highlight the input file and write the result to <outfile>.
If no input file is given, use stdin, if -o is not given, use stdout.
2014-12-01 06:10:30 +00:00
If -s is passed, lexing will be done in "streaming" mode, reading and
highlighting one line at a time. This will only work properly with
lexers that have no constructs spanning multiple lines!
<lexer> is a lexer name (query all lexer names with -L). If -l is not
given, the lexer is guessed from the extension of the input file name
(this obviously doesn't work if the input is stdin). If -g is passed,
attempt to guess the lexer from the file contents, or pass through as
plain text if this fails (this can work for stdin).
Likewise, <formatter> is a formatter name, and will be guessed from
the extension of the output file name. If no output file is given,
the terminal formatter will be used by default.
2017-02-14 07:25:51 +00:00
The additional option -x allows custom lexers and formatters to be
loaded from a .py file relative to the current working directory. For
example, ``-l ./customlexer.py -x``. By default, this option expects a
file with a class named CustomLexer or CustomFormatter; you can also
specify your own class name with a colon (``-l ./lexer.py:MyLexer``).
Users should be very careful not to use this option with untrusted files,
because it will import and run them.
With the -O option, you can give the lexer and formatter a comma-
separated list of options, e.g. ``-O bg=light,python=cool``.
The -P option adds lexer and formatter options like the -O option, but
you can only give one option per -P. That way, the option value may
contain commas and equals signs, which it can't with -O, e.g.
``-P "heading=Pygments, the Python highlighter".
With the -F option, you can add filters to the token stream, you can
give options in the same way as for -O after a colon (note: there must
not be spaces around the colon).
The -O, -P and -F options can be given multiple times.
With the -S option, print out style definitions for style <style>
for formatter <formatter>. The argument given by -a is formatter
dependent.
The -L option lists lexers, formatters, styles or filters -- set
`which` to the thing you want to list (e.g. "styles"), or omit it to
list everything.
The -N option guesses and prints out a lexer name based solely on
the given filename. It does not take input or highlight anything.
If no specific lexer can be determined "text" is returned.
The -H option prints detailed help for the object <name> of type <type>,
where <type> is one of "lexer", "formatter" or "filter".
2014-12-01 06:10:30 +00:00
The -s option processes lines one at a time until EOF, rather than
waiting to process the entire file. This only works for stdin, and
is intended for streaming input such as you get from 'tail -f'.
Example usage: "tail -f sql.log | pygmentize -s -l sql"
2016-06-13 14:41:17 +00:00
The -v option prints a detailed traceback on unhandled exceptions,
which is useful for debugging and bug reports.
The -h option prints this help.
The -V option prints the package version.
"""
def _parse_options(o_strs):
opts = {}
if not o_strs:
return opts
for o_str in o_strs:
2016-06-13 14:41:17 +00:00
if not o_str.strip():
continue
o_args = o_str.split(',')
for o_arg in o_args:
o_arg = o_arg.strip()
try:
2014-12-01 06:10:30 +00:00
o_key, o_val = o_arg.split('=', 1)
o_key = o_key.strip()
o_val = o_val.strip()
except ValueError:
opts[o_arg] = True
else:
opts[o_key] = o_val
return opts
def _parse_filters(f_strs):
filters = []
if not f_strs:
return filters
for f_str in f_strs:
if ':' in f_str:
fname, fopts = f_str.split(':', 1)
filters.append((fname, _parse_options([fopts])))
else:
filters.append((f_str, {}))
return filters
def _print_help(what, name):
try:
if what == 'lexer':
2016-06-13 14:41:17 +00:00
cls = get_lexer_by_name(name)
2013-09-22 21:29:16 +00:00
print("Help on the %s lexer:" % cls.name)
print(dedent(cls.__doc__))
elif what == 'formatter':
cls = find_formatter_class(name)
2013-09-22 21:29:16 +00:00
print("Help on the %s formatter:" % cls.name)
print(dedent(cls.__doc__))
elif what == 'filter':
cls = find_filter_class(name)
2013-09-22 21:29:16 +00:00
print("Help on the %s filter:" % name)
print(dedent(cls.__doc__))
2016-06-13 14:41:17 +00:00
return 0
except (AttributeError, ValueError):
2013-09-22 21:29:16 +00:00
print("%s not found!" % what, file=sys.stderr)
2016-06-13 14:41:17 +00:00
return 1
def _print_list(what):
if what == 'lexer':
2013-09-22 21:48:55 +00:00
print()
2013-09-22 21:29:16 +00:00
print("Lexers:")
print("~~~~~~~")
info = []
for fullname, names, exts, _ in get_all_lexers():
tup = (', '.join(names)+':', fullname,
exts and '(filenames ' + ', '.join(exts) + ')' or '')
info.append(tup)
info.sort()
for i in info:
2013-09-22 21:29:16 +00:00
print(('* %s\n %s %s') % i)
elif what == 'formatter':
2013-09-22 21:48:55 +00:00
print()
2013-09-22 21:29:16 +00:00
print("Formatters:")
print("~~~~~~~~~~~")
info = []
for cls in get_all_formatters():
doc = docstring_headline(cls)
tup = (', '.join(cls.aliases) + ':', doc, cls.filenames and
'(filenames ' + ', '.join(cls.filenames) + ')' or '')
info.append(tup)
info.sort()
for i in info:
2013-09-22 21:29:16 +00:00
print(('* %s\n %s %s') % i)
elif what == 'filter':
2013-09-22 21:48:55 +00:00
print()
2013-09-22 21:29:16 +00:00
print("Filters:")
print("~~~~~~~~")
for name in get_all_filters():
cls = find_filter_class(name)
2013-09-22 21:29:16 +00:00
print("* " + name + ':')
print(" %s" % docstring_headline(cls))
elif what == 'style':
2013-09-22 21:48:55 +00:00
print()
2013-09-22 21:29:16 +00:00
print("Styles:")
print("~~~~~~~")
for name in get_all_styles():
cls = get_style_by_name(name)
2013-09-22 21:29:16 +00:00
print("* " + name + ':')
print(" %s" % docstring_headline(cls))
2016-06-13 14:41:17 +00:00
def main_inner(popts, args, usage):
opts = {}
O_opts = []
P_opts = []
F_opts = []
for opt, arg in popts:
if opt == '-O':
O_opts.append(arg)
elif opt == '-P':
P_opts.append(arg)
elif opt == '-F':
F_opts.append(arg)
opts[opt] = arg
if opts.pop('-h', None) is not None:
2013-09-22 21:29:16 +00:00
print(usage)
return 0
if opts.pop('-V', None) is not None:
2017-02-14 07:25:51 +00:00
print('Pygments version %s, (c) 2006-2017 by Georg Brandl.' % __version__)
return 0
# handle ``pygmentize -L``
L_opt = opts.pop('-L', None)
if L_opt is not None:
if opts:
2013-09-22 21:29:16 +00:00
print(usage, file=sys.stderr)
return 2
2013-09-22 21:48:55 +00:00
# print version
main(['', '-V'])
if not args:
args = ['lexer', 'formatter', 'filter', 'style']
for arg in args:
_print_list(arg.rstrip('s'))
return 0
# handle ``pygmentize -H``
H_opt = opts.pop('-H', None)
if H_opt is not None:
if opts or len(args) != 2:
2013-09-22 21:29:16 +00:00
print(usage, file=sys.stderr)
return 2
2016-06-13 14:41:17 +00:00
what, name = args # pylint: disable=unbalanced-tuple-unpacking
if what not in ('lexer', 'formatter', 'filter'):
2013-09-22 21:29:16 +00:00
print(usage, file=sys.stderr)
return 2
2016-06-13 14:41:17 +00:00
return _print_help(what, name)
# parse -O options
parsed_opts = _parse_options(O_opts)
opts.pop('-O', None)
# parse -P options
for p_opt in P_opts:
try:
name, value = p_opt.split('=', 1)
except ValueError:
parsed_opts[p_opt] = True
else:
parsed_opts[name] = value
opts.pop('-P', None)
2014-12-01 06:10:30 +00:00
# encodings
2016-06-13 14:41:17 +00:00
inencoding = parsed_opts.get('inencoding', parsed_opts.get('encoding'))
2014-12-01 06:10:30 +00:00
outencoding = parsed_opts.get('outencoding', parsed_opts.get('encoding'))
# handle ``pygmentize -N``
infn = opts.pop('-N', None)
if infn is not None:
2016-06-13 14:41:17 +00:00
lexer = find_lexer_class_for_filename(infn)
if lexer is None:
lexer = TextLexer
2013-09-22 21:29:16 +00:00
print(lexer.aliases[0])
return 0
# handle ``pygmentize -S``
S_opt = opts.pop('-S', None)
a_opt = opts.pop('-a', None)
if S_opt is not None:
f_opt = opts.pop('-f', None)
if not f_opt:
2013-09-22 21:29:16 +00:00
print(usage, file=sys.stderr)
return 2
if opts or args:
2013-09-22 21:29:16 +00:00
print(usage, file=sys.stderr)
return 2
try:
parsed_opts['style'] = S_opt
fmter = get_formatter_by_name(f_opt, **parsed_opts)
2013-09-22 21:14:59 +00:00
except ClassNotFound as err:
2013-09-22 21:29:16 +00:00
print(err, file=sys.stderr)
return 1
2016-06-13 14:41:17 +00:00
print(fmter.get_style_defs(a_opt or ''))
return 0
# if no -S is given, -a is not allowed
if a_opt is not None:
2013-09-22 21:29:16 +00:00
print(usage, file=sys.stderr)
return 2
# parse -F options
F_opts = _parse_filters(F_opts)
opts.pop('-F', None)
2017-02-14 07:25:51 +00:00
allow_custom_lexer_formatter = False
# -x: allow custom (eXternal) lexers and formatters
if opts.pop('-x', None) is not None:
allow_custom_lexer_formatter = True
# select lexer
2016-06-13 14:41:17 +00:00
lexer = None
# given by name?
lexername = opts.pop('-l', None)
if lexername:
2017-02-14 07:25:51 +00:00
# custom lexer, located relative to user's cwd
if allow_custom_lexer_formatter and '.py' in lexername:
try:
if ':' in lexername:
filename, name = lexername.rsplit(':', 1)
lexer = load_lexer_from_file(filename, name,
**parsed_opts)
else:
lexer = load_lexer_from_file(lexername, **parsed_opts)
except ClassNotFound as err:
print('Error:', err, file=sys.stderr)
return 1
else:
try:
lexer = get_lexer_by_name(lexername, **parsed_opts)
except (OptionError, ClassNotFound) as err:
print('Error:', err, file=sys.stderr)
return 1
2014-12-01 06:10:30 +00:00
# read input code
code = None
if args:
if len(args) > 1:
2013-09-22 21:29:16 +00:00
print(usage, file=sys.stderr)
return 2
2014-12-01 06:10:30 +00:00
if '-s' in opts:
print('Error: -s option not usable when input file specified',
file=sys.stderr)
2016-06-13 14:41:17 +00:00
return 2
2014-12-01 06:10:30 +00:00
infn = args[0]
try:
2014-12-01 06:10:30 +00:00
with open(infn, 'rb') as infp:
code = infp.read()
2013-09-22 21:14:59 +00:00
except Exception as err:
2013-09-22 21:29:16 +00:00
print('Error: cannot read infile:', err, file=sys.stderr)
return 1
2014-12-01 06:10:30 +00:00
if not inencoding:
code, inencoding = guess_decode(code)
2014-12-01 06:10:30 +00:00
# do we have to guess the lexer?
if not lexer:
try:
lexer = get_lexer_for_filename(infn, code, **parsed_opts)
2013-09-22 21:14:59 +00:00
except ClassNotFound as err:
if '-g' in opts:
try:
lexer = guess_lexer(code, **parsed_opts)
except ClassNotFound:
lexer = TextLexer(**parsed_opts)
else:
2013-09-22 21:29:16 +00:00
print('Error:', err, file=sys.stderr)
return 1
2013-09-22 21:14:59 +00:00
except OptionError as err:
2013-09-22 21:29:16 +00:00
print('Error:', err, file=sys.stderr)
return 1
2014-12-01 06:10:30 +00:00
elif '-s' not in opts: # treat stdin as full file (-s support is later)
# read code from terminal, always in binary mode since we want to
# decode ourselves and be tolerant with it
if sys.version_info > (3,):
# Python 3: we have to use .buffer to get a binary stream
code = sys.stdin.buffer.read()
else:
code = sys.stdin.read()
2014-12-01 06:10:30 +00:00
if not inencoding:
code, inencoding = guess_decode_from_terminal(code, sys.stdin)
# else the lexer will do the decoding
if not lexer:
try:
lexer = guess_lexer(code, **parsed_opts)
except ClassNotFound:
lexer = TextLexer(**parsed_opts)
2014-12-01 06:10:30 +00:00
2016-06-13 14:41:17 +00:00
else: # -s option needs a lexer with -l
if not lexer:
print('Error: when using -s a lexer has to be selected with -l',
file=sys.stderr)
return 2
# process filters
for fname, fopts in F_opts:
try:
lexer.add_filter(fname, **fopts)
except ClassNotFound as err:
print('Error:', err, file=sys.stderr)
return 1
2014-12-01 06:10:30 +00:00
# select formatter
outfn = opts.pop('-o', None)
fmter = opts.pop('-f', None)
if fmter:
2017-02-14 07:25:51 +00:00
# custom formatter, located relative to user's cwd
if allow_custom_lexer_formatter and '.py' in fmter:
try:
if ':' in fmter:
file, fmtername = fmter.rsplit(':', 1)
fmter = load_formatter_from_file(file, fmtername,
**parsed_opts)
else:
fmter = load_formatter_from_file(fmter, **parsed_opts)
except ClassNotFound as err:
print('Error:', err, file=sys.stderr)
return 1
else:
try:
fmter = get_formatter_by_name(fmter, **parsed_opts)
except (OptionError, ClassNotFound) as err:
print('Error:', err, file=sys.stderr)
return 1
2014-12-01 06:10:30 +00:00
if outfn:
if not fmter:
try:
fmter = get_formatter_for_filename(outfn, **parsed_opts)
except (OptionError, ClassNotFound) as err:
print('Error:', err, file=sys.stderr)
return 1
try:
outfile = open(outfn, 'wb')
except Exception as err:
print('Error: cannot open outfile:', err, file=sys.stderr)
return 1
else:
if not fmter:
2017-02-14 07:25:51 +00:00
if '256' in os.environ.get('TERM', ''):
fmter = Terminal256Formatter(**parsed_opts)
else:
fmter = TerminalFormatter(**parsed_opts)
2014-12-01 06:10:30 +00:00
if sys.version_info > (3,):
# Python 3: we have to use .buffer to get a binary stream
outfile = sys.stdout.buffer
else:
2014-12-01 06:10:30 +00:00
outfile = sys.stdout
2014-12-01 06:10:30 +00:00
# determine output encoding if not explicitly selected
if not outencoding:
if outfn:
2014-12-01 06:10:30 +00:00
# output file? use lexer encoding for now (can still be None)
fmter.encoding = inencoding
else:
# else use terminal encoding
fmter.encoding = terminal_encoding(sys.stdout)
# provide coloring under Windows, if possible
if not outfn and sys.platform in ('win32', 'cygwin') and \
2016-06-13 14:41:17 +00:00
fmter.name in ('Terminal', 'Terminal256'): # pragma: no cover
2014-12-01 06:10:30 +00:00
# unfortunately colorama doesn't support binary streams on Py3
if sys.version_info > (3,):
2016-06-13 14:41:17 +00:00
from pygments.util import UnclosingTextIOWrapper
outfile = UnclosingTextIOWrapper(outfile, encoding=fmter.encoding)
2014-12-01 06:10:30 +00:00
fmter.encoding = None
try:
import colorama.initialise
except ImportError:
pass
else:
2014-12-01 06:10:30 +00:00
outfile = colorama.initialise.wrap_stream(
outfile, convert=None, strip=None, autoreset=False, wrap=True)
# When using the LaTeX formatter and the option `escapeinside` is
# specified, we need a special lexer which collects escaped text
# before running the chosen language lexer.
escapeinside = parsed_opts.get('escapeinside', '')
if len(escapeinside) == 2 and isinstance(fmter, LatexFormatter):
left = escapeinside[0]
right = escapeinside[1]
lexer = LatexEmbeddedLexer(left, right, lexer)
# ... and do it!
2016-06-13 14:41:17 +00:00
if '-s' not in opts:
# process whole input as per normal...
highlight(code, lexer, fmter, outfile)
return 0
else:
# line by line processing of stdin (eg: for 'tail -f')...
try:
while 1:
if sys.version_info > (3,):
# Python 3: we have to use .buffer to get a binary stream
line = sys.stdin.buffer.readline()
else:
line = sys.stdin.readline()
if not line:
break
if not inencoding:
line = guess_decode_from_terminal(line, sys.stdin)[0]
highlight(line, lexer, fmter, outfile)
if hasattr(outfile, 'flush'):
outfile.flush()
return 0
except KeyboardInterrupt: # pragma: no cover
return 0
2014-12-01 06:10:30 +00:00
2016-06-13 14:41:17 +00:00
def main(args=sys.argv):
"""
Main command line entry point.
"""
usage = USAGE % ((args[0],) * 6)
try:
2017-02-14 07:25:51 +00:00
popts, args = getopt.getopt(args[1:], "l:f:F:o:O:P:LS:a:N:vhVHgsx")
2016-06-13 14:41:17 +00:00
except getopt.GetoptError:
print(usage, file=sys.stderr)
return 2
try:
return main_inner(popts, args, usage)
2014-12-01 06:10:30 +00:00
except Exception:
2016-06-13 14:41:17 +00:00
if '-v' in dict(popts):
print(file=sys.stderr)
print('*' * 65, file=sys.stderr)
print('An unhandled exception occurred while highlighting.',
file=sys.stderr)
print('Please report the whole traceback to the issue tracker at',
file=sys.stderr)
print('<https://bitbucket.org/birkenfeld/pygments-main/issues>.',
file=sys.stderr)
print('*' * 65, file=sys.stderr)
print(file=sys.stderr)
raise
import traceback
info = traceback.format_exception(*sys.exc_info())
msg = info[-1].strip()
if len(info) >= 3:
# extract relevant file and position info
msg += '\n (f%s)' % info[-2].split('\n')[0].strip()[1:]
2013-09-22 21:48:55 +00:00
print(file=sys.stderr)
2013-09-22 21:29:16 +00:00
print('*** Error while highlighting:', file=sys.stderr)
print(msg, file=sys.stderr)
2016-06-13 14:41:17 +00:00
print('*** If this is a bug you want to report, please rerun with -v.',
file=sys.stderr)
return 1