# -*- coding: utf-8 -*-""" sphinx.highlighting ~~~~~~~~~~~~~~~~~~~ Highlight code blocks using Pygments. :copyright: Copyright 2007-2013 by the Sphinx team, see AUTHORS. :license: BSD, see LICENSE for details."""importsysimportreimporttextwraptry:importparserexceptImportError:# parser is not available on Jythonparser=Nonefromsphinx.util.pycompatimporthtmlescapefromsphinx.util.texescapeimporttex_hl_escape_map_newfromsphinx.extimportdoctesttry:importpygmentsfrompygmentsimporthighlightfrompygments.lexersimportPythonLexer,PythonConsoleLexer,CLexer, \
TextLexer,RstLexerfrompygments.lexersimportget_lexer_by_name,guess_lexerfrompygments.formattersimportHtmlFormatter,LatexFormatterfrompygments.filtersimportErrorTokenfrompygments.stylesimportget_style_by_namefrompygments.utilimportClassNotFoundfromsphinx.pygments_stylesimportSphinxStyle,NoneStyleexceptImportError:pygments=Nonelexers=NoneHtmlFormatter=LatexFormatter=Noneelse:lexers=dict(none=TextLexer(),python=PythonLexer(),pycon=PythonConsoleLexer(),pycon3=PythonConsoleLexer(python3=True),rest=RstLexer(),c=CLexer(),)for_lexerinlexers.values():_lexer.add_filter('raiseonerror')escape_hl_chars={ord(u'\\'):u'\\PYGZbs{}',ord(u'{'):u'\\PYGZob{}',ord(u'}'):u'\\PYGZcb{}'}# used if Pygments is not available_LATEX_STYLES=r'''\newcommand\PYGZbs{\char`\\}\newcommand\PYGZob{\char`\{}\newcommand\PYGZcb{\char`\}}'''parsing_exceptions=(SyntaxError,UnicodeEncodeError)ifsys.version_info<(2,5):# Python <= 2.4 raises MemoryError when parsing an# invalid encoding cookieparsing_exceptions+=MemoryError,classPygmentsBridge(object):# Set these attributes if you want to have different Pygments formatters# than the default ones.html_formatter=HtmlFormatterlatex_formatter=LatexFormatterdef__init__(self,dest='html',stylename='sphinx',trim_doctest_flags=False):self.dest=destifnotpygments:returnifstylenameisNoneorstylename=='sphinx':style=SphinxStyleelifstylename=='none':style=NoneStyleelif'.'instylename:module,stylename=stylename.rsplit('.',1)style=getattr(__import__(module,None,None,['__name__']),stylename)else:style=get_style_by_name(stylename)self.trim_doctest_flags=trim_doctest_flagsself.formatter_args={'style':style}ifdest=='html':self.formatter=self.html_formatterelse:self.formatter=self.latex_formatterself.formatter_args['commandprefix']='PYG'defget_formatter(self,**kwargs):kwargs.update(self.formatter_args)returnself.formatter(**kwargs)defunhighlighted(self,source):ifself.dest=='html':return'<pre>'+htmlescape(source)+'</pre>\n'else:# first, escape highlighting characters like Pygments doessource=source.translate(escape_hl_chars)# then, escape all characters nonrepresentable in LaTeXsource=source.translate(tex_hl_escape_map_new)return'\\begin{Verbatim}[commandchars=\\\\\\{\\}]\n'+ \
source+'\\end{Verbatim}\n'deftry_parse(self,src):# Make sure it ends in a newlinesrc+='\n'# Ignore consistent indentation.ifsrc.lstrip('\n').startswith(' '):src=textwrap.dedent(src)# 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 suitesrc=re.sub(r"(?m)^(\s*)"+mark+"(.)",r"\1"+mark+r"# \2",src)# if we're using 2.5, use the with statementifsys.version_info>=(2,5):src='from __future__ import with_statement\n'+srcifsys.version_info<(3,0)andisinstance(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')if(3,0)<=sys.version_info<(3,2):# Python 3.1 can't process '\r' as linesep.# `parser.suite("print('hello')\r\n")` cause error.if'\r\n'insrc:src=src.replace('\r\n','\n')ifparserisNone:returnTruetry:parser.suite(src)exceptparsing_exceptions:returnFalseelse:returnTruedefhighlight_block(self,source,lang,warn=None,force=False,**kwargs):ifnotisinstance(source,unicode):source=source.decode()ifnotpygments:returnself.unhighlighted(source)# find out which lexer to useiflangin('py','python'):ifsource.startswith('>>>'):# interactive sessionlexer=lexers['pycon']elifnotforce:# maybe Python -- try parsing itifself.try_parse(source):lexer=lexers['python']else:returnself.unhighlighted(source)else:lexer=lexers['python']eliflangin('python3','py3')andsource.startswith('>>>'):# for py3, recognize interactive sessions, but do not try parsing...lexer=lexers['pycon3']eliflang=='guess':try:lexer=guess_lexer(source)exceptException:returnself.unhighlighted(source)else:iflanginlexers:lexer=lexers[lang]else:try:lexer=lexers[lang]=get_lexer_by_name(lang)exceptClassNotFound:ifwarn:warn('Pygments lexer name %r is not known'%lang)returnself.unhighlighted(source)else:raiseelse:lexer.add_filter('raiseonerror')# trim doctest options if wantedifisinstance(lexer,PythonConsoleLexer)andself.trim_doctest_flags:source=doctest.blankline_re.sub('',source)source=doctest.doctestopt_re.sub('',source)# highlight via Pygmentstry:formatter=self.get_formatter(**kwargs)hlsource=highlight(source,lexer,formatter)ifself.dest=='html':returnhlsourceelse:ifnotisinstance(hlsource,unicode):# Py2 / Pygments < 1.6hlsource=hlsource.decode()returnhlsource.translate(tex_hl_escape_map_new)exceptErrorToken:# this is most probably not the selected language,# so let it pass unhighlightedreturnself.unhighlighted(source)defget_stylesheet(self):ifnotpygments:ifself.dest=='latex':return_LATEX_STYLES# no HTML styles neededreturn''formatter=self.get_formatter()ifself.dest=='html':returnformatter.get_style_defs('.highlight')else:returnformatter.get_style_defs()