Source

sphinx / sphinx / highlighting.py

Full commit
georg.brandl df246e3 






georg.brandl d36613a 

georg.brandl df246e3 

georg.brandl 60f4650 
georg.brandl df246e3 
georg.brandl f07e28f 
georg.brandl 6975feb 
georg.brandl df246e3 
georg.brandl f1c885f 

georg.brandl df246e3 




georg.brandl bd4fce9 
georg.brandl 46023b4 
georg.brandl df246e3 

georg.brandl f07e28f 
georg.brandl df246e3 
georg.brandl 3ec7a08 
georg.brandl df246e3 


georg.brandl f07e28f 
georg.brandl df246e3 
georg.brandl f07e28f 

georg.brandl df246e3 






georg.brandl 86a4ec1 
georg.brandl df246e3 
georg.brandl 3ec7a08 
georg.brandl df246e3 

georg.brandl 60f4650 
georg.brandl df246e3 


georg.brandl d539975 


georg.brandl df246e3 






georg.brandl 4368135 


georg.brandl 142ced7 
georg.brandl 5938262 

georg.brandl 4368135 


georg.brandl 5938262 

georg.brandl 142ced7 
georg.brandl f094117 






georg.brandl f07e28f 

georg.brandl 4aca791 
georg.brandl f07e28f 



armin.ronacher 6579c5e 



armin.ronacher 0717252 
georg.brandl 4aff63b 

georg.brandl 22ddbd1 


georg.brandl df246e3 
georg.brandl f1c885f 










georg.brandl d539975 































georg.brandl 4aff63b 
georg.brandl f07e28f 
georg.brandl f1c885f 
georg.brandl d539975 
georg.brandl f07e28f 




georg.brandl d539975 


georg.brandl f1c885f 
georg.brandl d539975 


georg.brandl bd4fce9 




georg.brandl 46023b4 
georg.brandl 4aff63b 




georg.brandl f07e28f 
georg.brandl f1c885f 




georg.brandl f07e28f 
georg.brandl 65eb4b9 

georg.brandl f1c885f 
georg.brandl 5c13207 
georg.brandl f07e28f 

georg.brandl 5938262 


georg.brandl f07e28f 
georg.brandl 4368135 






# -*- coding: utf-8 -*-
"""
    sphinx.highlighting
    ~~~~~~~~~~~~~~~~~~~

    Highlight code blocks using Pygments.

    :copyright: 2007-2008 by Georg Brandl.
    :license: BSD.
"""

import sys
import cgi
import re
import parser

from sphinx.util.texescape import tex_hl_escape_map

try:
    import pygments
    from pygments import highlight
    from pygments.lexers import PythonLexer, PythonConsoleLexer, CLexer, \
         TextLexer, RstLexer
    from pygments.lexers import get_lexer_by_name, guess_lexer
    from pygments.formatters import HtmlFormatter, LatexFormatter
    from pygments.filters import ErrorToken
    from pygments.style import Style
    from pygments.styles import get_style_by_name
    from pygments.styles.friendly import FriendlyStyle
    from pygments.token import Generic, Comment, Number
except ImportError:
    pygments = None
else:
    class SphinxStyle(Style):
        """
        Like friendly, but a bit darker to enhance contrast on the green
        background.
        """

        background_color = '#eeffcc'
        default_style = ''

        styles = FriendlyStyle.styles
        styles.update({
            Generic.Output: '#333',
            Comment: 'italic #408090',
            Number: '#208050',
        })

    lexers = dict(
        none = TextLexer(),
        python = PythonLexer(),
        pycon = PythonConsoleLexer(),
        # the python3 option exists as of Pygments 0.12, but it doesn't
        # do any harm in previous versions
        pycon3 = PythonConsoleLexer(python3=True),
        rest = RstLexer(),
        c = CLexer(),
    )
    for _lexer in lexers.values():
        _lexer.add_filter('raiseonerror')


escape_hl_chars = {ord(u'@'): u'@PYGZat[]',
                   ord(u'['): u'@PYGZlb[]',
                   ord(u']'): u'@PYGZrb[]'}

# used if Pygments is not available
_LATEX_STYLES = r'''
\newcommand\PYGZat{@}
\newcommand\PYGZlb{[}
\newcommand\PYGZrb{]}
'''


parsing_exceptions = (SyntaxError, UnicodeEncodeError)
if sys.version_info < (2, 5):
    # Python <= 2.4 raises MemoryError when parsing an
    # invalid encoding cookie
    parsing_exceptions += MemoryError,


class PygmentsBridge(object):
    def __init__(self, dest='html', stylename='sphinx'):
        self.dest = dest
        if not pygments:
            return
        if stylename == 'sphinx':
            style = SphinxStyle
        elif '.' in stylename:
            module, stylename = stylename.rsplit('.', 1)
            style = getattr(__import__(module, None, None, ['']), stylename)
        else:
            style = get_style_by_name(stylename)
        self.hfmter = {False: HtmlFormatter(style=style),
                       True: HtmlFormatter(style=style, linenos=True)}
        self.lfmter = {False: LatexFormatter(style=style, commandprefix='PYG'),
                       True: LatexFormatter(style=style, linenos=True,
                                            commandprefix='PYG')}

    def unhighlighted(self, source):
        if self.dest == 'html':
            return '<pre>' + cgi.escape(source) + '</pre>\n'
        else:
            # first, escape highlighting characters like Pygments does
            source = source.translate(escape_hl_chars)
            # then, escape all characters nonrepresentable in LaTeX
            source = source.translate(tex_hl_escape_map)
            return '\\begin{Verbatim}[commandchars=@\\[\\]]\n' + \
                   source + '\\end{Verbatim}\n'

    def try_parse(self, src):
        # Make sure it ends in a newline
        src += '\n'

        # Replace "..." by a mark which is also a valid python expression
        # (Note, the highlighter gets the original source, this is only done
        #  to allow "..." in code and still highlight it as Python code.)
        mark = "__highlighting__ellipsis__"
        src = src.replace("...", mark)

        # lines beginning with "..." are probably placeholders for suite
        src = re.sub(r"(?m)^(\s*)" + mark + "(.)", r"\1"+ mark + r"# \2", src)

        # if we're using 2.5, use the with statement
        if sys.version_info >= (2, 5):
            src = 'from __future__ import with_statement\n' + src

        if isinstance(src, unicode):
            # Non-ASCII chars will only occur in string literals
            # and comments.  If we wanted to give them to the parser
            # correctly, we'd have to find out the correct source
            # encoding.  Since it may not even be given in a snippet,
            # just replace all non-ASCII characters.
            src = src.encode('ascii', 'replace')

        try:
            parser.suite(src)
        except parsing_exceptions:
            return False
        else:
            return True

    def highlight_block(self, source, lang, linenos=False):
        if not pygments:
            return self.unhighlighted(source)
        if lang in ('py', 'python'):
            if source.startswith('>>>'):
                # interactive session
                lexer = lexers['pycon']
            else:
                # maybe Python -- try parsing it
                if self.try_parse(source):
                    lexer = lexers['python']
                else:
                    return self.unhighlighted(source)
        elif lang in ('python3', 'py3') and source.startswith('>>>'):
            # for py3, recognize interactive sessions, but do not try parsing...
            lexer = lexers['pycon3']
        elif lang == 'guess':
            try:
                lexer = guess_lexer(source)
            except Exception:
                return self.unhighlighted(source)
        else:
            if lang in lexers:
                lexer = lexers[lang]
            else:
                lexer = lexers[lang] = get_lexer_by_name(lang)
                lexer.add_filter('raiseonerror')
        try:
            if self.dest == 'html':
                return highlight(source, lexer, self.hfmter[bool(linenos)])
            else:
                hlsource = highlight(source, lexer, self.lfmter[bool(linenos)])
                return hlsource.translate(tex_hl_escape_map)
        except ErrorToken:
            # this is most probably not the selected language,
            # so let it pass unhighlighted
            return self.unhighlighted(source)

    def get_stylesheet(self):
        if not pygments:
            if self.dest == 'latex':
                return _LATEX_STYLES
            # no HTML styles needed
            return ''
        if self.dest == 'html':
            return self.hfmter[0].get_style_defs()
        else:
            styledefs = self.lfmter[0].get_style_defs()
            # workaround for Pygments < 0.12
            if styledefs.startswith('\\newcommand\\at{@}'):
                styledefs += _LATEX_STYLES
            return styledefs