Back to index

moin  1.9.0~rc2
cmdline.py
Go to the documentation of this file.
00001 # -*- coding: utf-8 -*-
00002 """
00003     pygments.cmdline
00004     ~~~~~~~~~~~~~~~~
00005 
00006     Command line interface.
00007 
00008     :copyright: Copyright 2006-2009 by the Pygments team, see AUTHORS.
00009     :license: BSD, see LICENSE for details.
00010 """
00011 import sys
00012 import getopt
00013 from textwrap import dedent
00014 
00015 from pygments import __version__, highlight
00016 from pygments.util import ClassNotFound, OptionError, docstring_headline
00017 from pygments.lexers import get_all_lexers, get_lexer_by_name, get_lexer_for_filename, \
00018      find_lexer_class, guess_lexer, TextLexer
00019 from pygments.formatters import get_all_formatters, get_formatter_by_name, \
00020      get_formatter_for_filename, find_formatter_class, \
00021      TerminalFormatter  # pylint:disable-msg=E0611
00022 from pygments.filters import get_all_filters, find_filter_class
00023 from pygments.styles import get_all_styles, get_style_by_name
00024 
00025 
00026 USAGE = """\
00027 Usage: %s [-l <lexer> | -g] [-F <filter>[:<options>]] [-f <formatter>]
00028           [-O <options>] [-P <option=value>] [-o <outfile>] [<infile>]
00029 
00030        %s -S <style> -f <formatter> [-a <arg>] [-O <options>] [-P <option=value>]
00031        %s -L [<which> ...]
00032        %s -N <filename>
00033        %s -H <type> <name>
00034        %s -h | -V
00035 
00036 Highlight the input file and write the result to <outfile>.
00037 
00038 If no input file is given, use stdin, if -o is not given, use stdout.
00039 
00040 <lexer> is a lexer name (query all lexer names with -L). If -l is not
00041 given, the lexer is guessed from the extension of the input file name
00042 (this obviously doesn't work if the input is stdin).  If -g is passed,
00043 attempt to guess the lexer from the file contents, or pass through as
00044 plain text if this fails (this can work for stdin).
00045 
00046 Likewise, <formatter> is a formatter name, and will be guessed from
00047 the extension of the output file name. If no output file is given,
00048 the terminal formatter will be used by default.
00049 
00050 With the -O option, you can give the lexer and formatter a comma-
00051 separated list of options, e.g. ``-O bg=light,python=cool``.
00052 
00053 The -P option adds lexer and formatter options like the -O option, but
00054 you can only give one option per -P. That way, the option value may
00055 contain commas and equals signs, which it can't with -O, e.g.
00056 ``-P "heading=Pygments, the Python highlighter".
00057 
00058 With the -F option, you can add filters to the token stream, you can
00059 give options in the same way as for -O after a colon (note: there must
00060 not be spaces around the colon).
00061 
00062 The -O, -P and -F options can be given multiple times.
00063 
00064 With the -S option, print out style definitions for style <style>
00065 for formatter <formatter>. The argument given by -a is formatter
00066 dependent.
00067 
00068 The -L option lists lexers, formatters, styles or filters -- set
00069 `which` to the thing you want to list (e.g. "styles"), or omit it to
00070 list everything.
00071 
00072 The -N option guesses and prints out a lexer name based solely on
00073 the given filename. It does not take input or highlight anything.
00074 If no specific lexer can be determined "text" is returned.
00075 
00076 The -H option prints detailed help for the object <name> of type <type>,
00077 where <type> is one of "lexer", "formatter" or "filter".
00078 
00079 The -h option prints this help.
00080 The -V option prints the package version.
00081 """
00082 
00083 
00084 def _parse_options(o_strs):
00085     opts = {}
00086     if not o_strs:
00087         return opts
00088     for o_str in o_strs:
00089         if not o_str:
00090             continue
00091         o_args = o_str.split(',')
00092         for o_arg in o_args:
00093             o_arg = o_arg.strip()
00094             try:
00095                 o_key, o_val = o_arg.split('=')
00096                 o_key = o_key.strip()
00097                 o_val = o_val.strip()
00098             except ValueError:
00099                 opts[o_arg] = True
00100             else:
00101                 opts[o_key] = o_val
00102     return opts
00103 
00104 
00105 def _parse_filters(f_strs):
00106     filters = []
00107     if not f_strs:
00108         return filters
00109     for f_str in f_strs:
00110         if ':' in f_str:
00111             fname, fopts = f_str.split(':', 1)
00112             filters.append((fname, _parse_options([fopts])))
00113         else:
00114             filters.append((f_str, {}))
00115     return filters
00116 
00117 
00118 def _print_help(what, name):
00119     try:
00120         if what == 'lexer':
00121             cls = find_lexer_class(name)
00122             print "Help on the %s lexer:" % cls.name
00123             print dedent(cls.__doc__)
00124         elif what == 'formatter':
00125             cls = find_formatter_class(name)
00126             print "Help on the %s formatter:" % cls.name
00127             print dedent(cls.__doc__)
00128         elif what == 'filter':
00129             cls = find_filter_class(name)
00130             print "Help on the %s filter:" % name
00131             print dedent(cls.__doc__)
00132     except AttributeError:
00133         print >>sys.stderr, "%s not found!" % what
00134 
00135 
00136 def _print_list(what):
00137     if what == 'lexer':
00138         print
00139         print "Lexers:"
00140         print "~~~~~~~"
00141 
00142         info = []
00143         for fullname, names, exts, _ in get_all_lexers():
00144             tup = (', '.join(names)+':', fullname,
00145                    exts and '(filenames ' + ', '.join(exts) + ')' or '')
00146             info.append(tup)
00147         info.sort()
00148         for i in info:
00149             print ('* %s\n    %s %s') % i
00150 
00151     elif what == 'formatter':
00152         print
00153         print "Formatters:"
00154         print "~~~~~~~~~~~"
00155 
00156         info = []
00157         for cls in get_all_formatters():
00158             doc = docstring_headline(cls)
00159             tup = (', '.join(cls.aliases) + ':', doc, cls.filenames and
00160                    '(filenames ' + ', '.join(cls.filenames) + ')' or '')
00161             info.append(tup)
00162         info.sort()
00163         for i in info:
00164             print ('* %s\n    %s %s') % i
00165 
00166     elif what == 'filter':
00167         print
00168         print "Filters:"
00169         print "~~~~~~~~"
00170 
00171         for name in get_all_filters():
00172             cls = find_filter_class(name)
00173             print "* " + name + ':'
00174             print "    %s" % docstring_headline(cls)
00175 
00176     elif what == 'style':
00177         print
00178         print "Styles:"
00179         print "~~~~~~~"
00180 
00181         for name in get_all_styles():
00182             cls = get_style_by_name(name)
00183             print "* " + name + ':'
00184             print "    %s" % docstring_headline(cls)
00185 
00186 
00187 def main(args=sys.argv):
00188     """
00189     Main command line entry point.
00190     """
00191     # pylint: disable-msg=R0911,R0912,R0915
00192 
00193     usage = USAGE % ((args[0],) * 6)
00194 
00195     try:
00196         popts, args = getopt.getopt(args[1:], "l:f:F:o:O:P:LS:a:N:hVHg")
00197     except getopt.GetoptError, err:
00198         print >>sys.stderr, usage
00199         return 2
00200     opts = {}
00201     O_opts = []
00202     P_opts = []
00203     F_opts = []
00204     for opt, arg in popts:
00205         if opt == '-O':
00206             O_opts.append(arg)
00207         elif opt == '-P':
00208             P_opts.append(arg)
00209         elif opt == '-F':
00210             F_opts.append(arg)
00211         opts[opt] = arg
00212 
00213     if not opts and not args:
00214         print usage
00215         return 0
00216 
00217     if opts.pop('-h', None) is not None:
00218         print usage
00219         return 0
00220 
00221     if opts.pop('-V', None) is not None:
00222         print 'Pygments version %s, (c) 2006-2008 by Georg Brandl.' % __version__
00223         return 0
00224 
00225     # handle ``pygmentize -L``
00226     L_opt = opts.pop('-L', None)
00227     if L_opt is not None:
00228         if opts:
00229             print >>sys.stderr, usage
00230             return 2
00231 
00232         # print version
00233         main(['', '-V'])
00234         if not args:
00235             args = ['lexer', 'formatter', 'filter', 'style']
00236         for arg in args:
00237             _print_list(arg.rstrip('s'))
00238         return 0
00239 
00240     # handle ``pygmentize -H``
00241     H_opt = opts.pop('-H', None)
00242     if H_opt is not None:
00243         if opts or len(args) != 2:
00244             print >>sys.stderr, usage
00245             return 2
00246 
00247         what, name = args
00248         if what not in ('lexer', 'formatter', 'filter'):
00249             print >>sys.stderr, usage
00250             return 2
00251 
00252         _print_help(what, name)
00253         return 0
00254 
00255     # parse -O options
00256     parsed_opts = _parse_options(O_opts)
00257     opts.pop('-O', None)
00258 
00259     # parse -P options
00260     for p_opt in P_opts:
00261         try:
00262             name, value = p_opt.split('=', 1)
00263         except ValueError:
00264             parsed_opts[p_opt] = True
00265         else:
00266             parsed_opts[name] = value
00267     opts.pop('-P', None)
00268 
00269     # handle ``pygmentize -N``
00270     infn = opts.pop('-N', None)
00271     if infn is not None:
00272         try:
00273             lexer = get_lexer_for_filename(infn, **parsed_opts)
00274         except ClassNotFound, err:
00275             lexer = TextLexer()
00276         except OptionError, err:
00277             print >>sys.stderr, 'Error:', err
00278             return 1
00279 
00280         print lexer.aliases[0]
00281         return 0
00282 
00283     # handle ``pygmentize -S``
00284     S_opt = opts.pop('-S', None)
00285     a_opt = opts.pop('-a', None)
00286     if S_opt is not None:
00287         f_opt = opts.pop('-f', None)
00288         if not f_opt:
00289             print >>sys.stderr, usage
00290             return 2
00291         if opts or args:
00292             print >>sys.stderr, usage
00293             return 2
00294 
00295         try:
00296             parsed_opts['style'] = S_opt
00297             fmter = get_formatter_by_name(f_opt, **parsed_opts)
00298         except ClassNotFound, err:
00299             print >>sys.stderr, err
00300             return 1
00301 
00302         arg = a_opt or ''
00303         try:
00304             print fmter.get_style_defs(arg)
00305         except Exception, err:
00306             print >>sys.stderr, 'Error:', err
00307             return 1
00308         return 0
00309 
00310     # if no -S is given, -a is not allowed
00311     if a_opt is not None:
00312         print >>sys.stderr, usage
00313         return 2
00314 
00315     # parse -F options
00316     F_opts = _parse_filters(F_opts)
00317     opts.pop('-F', None)
00318 
00319     # select formatter
00320     outfn = opts.pop('-o', None)
00321     fmter = opts.pop('-f', None)
00322     if fmter:
00323         try:
00324             fmter = get_formatter_by_name(fmter, **parsed_opts)
00325         except (OptionError, ClassNotFound), err:
00326             print >>sys.stderr, 'Error:', err
00327             return 1
00328 
00329     if outfn:
00330         if not fmter:
00331             try:
00332                 fmter = get_formatter_for_filename(outfn, **parsed_opts)
00333             except (OptionError, ClassNotFound), err:
00334                 print >>sys.stderr, 'Error:', err
00335                 return 1
00336         try:
00337             outfile = open(outfn, 'wb')
00338         except Exception, err:
00339             print >>sys.stderr, 'Error: cannot open outfile:', err
00340             return 1
00341     else:
00342         if not fmter:
00343             fmter = TerminalFormatter(**parsed_opts)
00344         outfile = sys.stdout
00345 
00346     # select lexer
00347     lexer = opts.pop('-l', None)
00348     if lexer:
00349         try:
00350             lexer = get_lexer_by_name(lexer, **parsed_opts)
00351         except (OptionError, ClassNotFound), err:
00352             print >>sys.stderr, 'Error:', err
00353             return 1
00354 
00355     if args:
00356         if len(args) > 1:
00357             print >>sys.stderr, usage
00358             return 2
00359 
00360         infn = args[0]
00361         try:
00362             code = open(infn, 'rb').read()
00363         except Exception, err:
00364             print >>sys.stderr, 'Error: cannot read infile:', err
00365             return 1
00366 
00367         if not lexer:
00368             try:
00369                 lexer = get_lexer_for_filename(infn, code, **parsed_opts)
00370             except ClassNotFound, err:
00371                 if '-g' in opts:
00372                     try:
00373                         lexer = guess_lexer(code)
00374                     except ClassNotFound:
00375                         lexer = TextLexer()
00376                 else:
00377                     print >>sys.stderr, 'Error:', err
00378                     return 1
00379             except OptionError, err:
00380                 print >>sys.stderr, 'Error:', err
00381                 return 1
00382 
00383     else:
00384         if '-g' in opts:
00385             code = sys.stdin.read()
00386             try:
00387                 lexer = guess_lexer(code)
00388             except ClassNotFound:
00389                 lexer = TextLexer()
00390         elif not lexer:
00391             print >>sys.stderr, 'Error: no lexer name given and reading ' + \
00392                                 'from stdin (try using -g or -l <lexer>)'
00393             return 2
00394         else:
00395             code = sys.stdin.read()
00396 
00397     # No encoding given? Use latin1 if output file given,
00398     # stdin/stdout encoding otherwise.
00399     # (This is a compromise, I'm not too happy with it...)
00400     if 'encoding' not in parsed_opts and 'outencoding' not in parsed_opts:
00401         if outfn:
00402             # encoding pass-through
00403             fmter.encoding = 'latin1'
00404         else:
00405             if sys.version_info < (3,):
00406                 # use terminal encoding; Python 3's terminals already do that
00407                 lexer.encoding = getattr(sys.stdin, 'encoding',
00408                                          None) or 'ascii'
00409                 fmter.encoding = getattr(sys.stdout, 'encoding',
00410                                          None) or 'ascii'
00411 
00412     # ... and do it!
00413     try:
00414         # process filters
00415         for fname, fopts in F_opts:
00416             lexer.add_filter(fname, **fopts)
00417         highlight(code, lexer, fmter, outfile)
00418     except Exception, err:
00419         import traceback
00420         info = traceback.format_exception(*sys.exc_info())
00421         msg = info[-1].strip()
00422         if len(info) >= 3:
00423             # extract relevant file and position info
00424             msg += '\n   (f%s)' % info[-2].split('\n')[0].strip()[1:]
00425         print >>sys.stderr
00426         print >>sys.stderr, '*** Error while highlighting:'
00427         print >>sys.stderr, msg
00428         return 1
00429 
00430     return 0