[Python-checkins] r65630 - in doctools/trunk: Makefile doc/markup/inline.rst sphinx/builder.py sphinx/environment.py sphinx/highlighting.py sphinx/latexwriter.py sphinx/util/texescape.py tests/test_markup.py

georg.brandl python-checkins at python.org
Sun Aug 10 18:59:28 CEST 2008


Author: georg.brandl
Date: Sun Aug 10 18:59:27 2008
New Revision: 65630

Log:
Merged revisions 65566-65567,65623,65625 via svnmerge from 
svn+ssh://pythondev@svn.python.org/doctools/branches/0.4.x

........
  r65566 | georg.brandl | 2008-08-07 09:11:11 +0000 (Thu, 07 Aug 2008) | 2 lines
  
  Clarification for the ref role.
........
  r65567 | georg.brandl | 2008-08-07 09:11:25 +0000 (Thu, 07 Aug 2008) | 2 lines
  
  Rebuild everything if extensions change.
........
  r65623 | georg.brandl | 2008-08-10 11:18:42 +0000 (Sun, 10 Aug 2008) | 2 lines
  
  Unify handling of LaTeX escaping, and add some more replacements.
........
  r65625 | georg.brandl | 2008-08-10 11:25:41 +0000 (Sun, 10 Aug 2008) | 2 lines
  
  Make tex escapes a module.
........


Added:
   doctools/trunk/sphinx/util/texescape.py
      - copied unchanged from r65625, /doctools/branches/0.4.x/sphinx/util/texescape.py
Modified:
   doctools/trunk/   (props changed)
   doctools/trunk/Makefile
   doctools/trunk/doc/markup/inline.rst
   doctools/trunk/sphinx/builder.py
   doctools/trunk/sphinx/environment.py
   doctools/trunk/sphinx/highlighting.py
   doctools/trunk/sphinx/latexwriter.py
   doctools/trunk/tests/test_markup.py

Modified: doctools/trunk/Makefile
==============================================================================
--- doctools/trunk/Makefile	(original)
+++ doctools/trunk/Makefile	Sun Aug 10 18:59:27 2008
@@ -4,7 +4,7 @@
 
 .PHONY: all check clean clean-pyc clean-patchfiles pylint reindent test
 
-all: clean-pyc check
+all: clean-pyc check test
 
 check:
 	@$(PYTHON) utils/check_sources.py -i sphinx/style/jquery.js sphinx

Modified: doctools/trunk/doc/markup/inline.rst
==============================================================================
--- doctools/trunk/doc/markup/inline.rst	(original)
+++ doctools/trunk/doc/markup/inline.rst	Sun Aug 10 18:59:27 2008
@@ -182,10 +182,12 @@
 Cross-referencing arbitrary locations
 -------------------------------------
 
-To support cross-referencing to arbitrary locations in the documentation, the
-standard reST labels used.  Of course, for this to work label names must be
-unique throughout the entire documentation.  There are two ways in which you can
-refer to labels:
+.. index:: pair: ref; role
+
+To support cross-referencing to arbitrary locations in any document, the
+standard reST labels are used.  For this to work label names must be unique
+throughout the entire documentation.  There are two ways in which you can refer
+to labels:
 
 * If you place a label directly before a section title, you can reference to it
   with ``:ref:`label-name```.  Example::
@@ -200,12 +202,17 @@
      It refers to the section itself, see :ref:`my-reference-label`.
 
   The ``:ref:`` role would then generate a link to the section, with the link
-  title being "Section to cross-reference".
+  title being "Section to cross-reference".  This works just as well when
+  section and reference are in different source files.
 
 * Labels that aren't placed before a section title can still be referenced to,
   but you must give the link an explicit title, using this syntax: ``:ref:`Link
   title <label-name>```.
 
+Using :role:`ref` is advised over standard reStructuredText links to sections
+(like ```Section title`_``) because it works across files, when section headings
+are changed, and for all builders that support cross-references.
+  
 
 Other semantic markup
 ---------------------

Modified: doctools/trunk/sphinx/builder.py
==============================================================================
--- doctools/trunk/sphinx/builder.py	(original)
+++ doctools/trunk/sphinx/builder.py	Sun Aug 10 18:59:27 2008
@@ -26,7 +26,7 @@
 from docutils.readers.doctree import Reader as DoctreeReader
 
 from sphinx import addnodes, locale, __version__
-from sphinx.util import ensuredir, relative_uri, SEP, os_path, json
+from sphinx.util import ensuredir, relative_uri, SEP, os_path, json, texescape
 from sphinx.htmlhelp import build_hhx
 from sphinx.htmlwriter import HTMLWriter, HTMLTranslator, SmartyPantsHTMLTranslator
 from sphinx.textwriter import TextWriter
@@ -899,6 +899,7 @@
     def init(self):
         self.docnames = []
         self.document_data = []
+        texescape.init()
 
     def get_outdated_docs(self):
         return 'all documents' # for now

Modified: doctools/trunk/sphinx/environment.py
==============================================================================
--- doctools/trunk/sphinx/environment.py	(original)
+++ doctools/trunk/sphinx/environment.py	Sun Aug 10 18:59:27 2008
@@ -418,6 +418,11 @@
                     break
             else:
                 msg = ''
+            # this value is not covered by the above loop because it is handled
+            # specially by the config class
+            if self.config.extensions != config.extensions:
+                msg = '[extensions changed] '
+                config_changed = True
         # the source and doctree directories may have been relocated
         self.srcdir = srcdir
         self.doctreedir = doctreedir

Modified: doctools/trunk/sphinx/highlighting.py
==============================================================================
--- doctools/trunk/sphinx/highlighting.py	(original)
+++ doctools/trunk/sphinx/highlighting.py	Sun Aug 10 18:59:27 2008
@@ -14,6 +14,8 @@
 import re
 import parser
 
+from sphinx.util.texescape import tex_hl_escape_map
+
 try:
     import pygments
     from pygments import highlight
@@ -56,14 +58,9 @@
         _lexer.add_filter('raiseonerror')
 
 
-
-def escape_tex(text):
-    return text.replace('@', '\x00').    \
-                replace('[', '\x01').    \
-                replace(']', '\x02').    \
-                replace('\x00', '@at[]').\
-                replace('\x01', '@lb[]').\
-                replace('\x02', '@rb[]')
+escape_hl_chars = {ord(u'@'): u'@at[]',
+                   ord(u'['): u'@lb[]',
+                   ord(u']'): u'@rb[]'}
 
 # used if Pygments is not available
 _LATEX_STYLES = r'''
@@ -98,15 +95,20 @@
                        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 highlight_block(self, source, lang, linenos=False):
-        def unhighlighted():
-            if self.dest == 'html':
-                return '<pre>' + cgi.escape(source) + '</pre>\n'
-            else:
-                return '\\begin{Verbatim}[commandchars=@\\[\\]]\n' + \
-                       escape_tex(source) + '\\end{Verbatim}\n'
         if not pygments:
-            return unhighlighted()
+            return self.unhighlighted(source)
         if lang == 'python':
             if source.startswith('>>>'):
                 # interactive session
@@ -138,7 +140,7 @@
                 try:
                     parser.suite(src)
                 except parsing_exceptions:
-                    return unhighlighted()
+                    return self.unhighlighted(source)
                 else:
                     lexer = lexers['python']
         else:
@@ -148,12 +150,15 @@
                 lexer = lexers[lang] = get_lexer_by_name(lang)
                 lexer.add_filter('raiseonerror')
         try:
-            fmter = (self.dest == 'html' and self.hfmter or self.lfmter)[bool(linenos)]
-            return highlight(source, lexer, fmter)
+            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 unhighlighted()
+            return self.unhighlighted(source)
 
     def get_stylesheet(self):
         if not pygments:

Modified: doctools/trunk/sphinx/latexwriter.py
==============================================================================
--- doctools/trunk/sphinx/latexwriter.py	(original)
+++ doctools/trunk/sphinx/latexwriter.py	Sun Aug 10 18:59:27 2008
@@ -23,6 +23,7 @@
 from sphinx import addnodes
 from sphinx import highlighting
 from sphinx.locale import admonitionlabels, versionlabels
+from sphinx.util.texescape import tex_escape_map
 from sphinx.util.smartypants import educateQuotesLatex
 
 HEADER = r'''%% Generated by Sphinx.
@@ -42,7 +43,7 @@
 
 BEGIN_DOC = r'''
 \begin{document}
-\shorthandoff{"}
+%(shorthandoff)s
 \maketitle
 \tableofcontents
 '''
@@ -87,6 +88,13 @@
 
 # Helper classes
 
+class ExtBabel(Babel):
+    def get_shorthandoff(self):
+        if self.language == 'de':
+            return '\\shorthandoff{"}'
+        return ''
+
+
 class Table(object):
     def __init__(self):
         self.col = 0
@@ -120,7 +128,7 @@
             paper = 'letterpaper'
         date = time.strftime(builder.config.today_fmt or _('%B %d, %Y'))
         logo = (builder.config.latex_logo and
-                "\\includegraphics{%s}\\par" % path.basename(builder.config.latex_logo)
+                '\\includegraphics{%s}\\par' % path.basename(builder.config.latex_logo)
                 or '')
         self.options = {'docclass': docclass,
                         'papersize': paper,
@@ -135,11 +143,13 @@
                         'releasename': _('Release'),
                         'logo': logo,
                         'date': date,
-                        'classoptions': '',
+                        'classoptions': ',english',
+                        'shorthandoff': '',
                         }
         if builder.config.language:
-            babel = Babel(builder.config.language)
+            babel = ExtBabel(builder.config.language)
             self.options['classoptions'] += ',' + babel.get_language()
+            self.shorthandoff = babel.get_shorthandoff()
         self.highlighter = highlighting.PygmentsBridge(
             'latex', builder.config.pygments_style)
         self.context = []
@@ -1045,42 +1055,12 @@
 
     # text handling
 
-    replacements = [
-        (u"\\", u"\x00"),
-        (u"$", ur"\$"),
-        (r"%", ur"\%"),
-        (u"&", ur"\&"),
-        (u"#", ur"\#"),
-        (u"_", ur"\_"),
-        (u"{", ur"\{"),
-        (u"}", ur"\}"),
-        (u"[", ur"{[}"),
-        (u"]", ur"{]}"),
-        (u"¶", ur"\P{}"),
-        (u"§", ur"\S{}"),
-        (u"∞", ur"$\infty$"),
-        (u"±", ur"$\pm$"),
-        (u"‣", ur"$\rightarrow$"),
-        (u"Ω", ur"$\Omega$"),
-        (u"Ω", ur"$\Omega$"),
-        (u"φ", ur"$\phi$"),
-        (u"π", ur"$\pi$"),
-        (u"~", ur"\textasciitilde{}"),
-        (u"€", ur"\texteuro{}"),
-        (u"<", ur"\textless{}"),
-        (u">", ur"\textgreater{}"),
-        (u"^", ur"\textasciicircum{}"),
-        (u"\x00", ur"\textbackslash{}"),
-        (u"\N{RIGHTWARDS ARROW}", ur"$\rightarrow$"),
-    ]
-
     def encode(self, text):
-        for x, y in self.replacements:
-            text = text.replace(x, y)
+        text = unicode(text).translate(tex_escape_map)
         if self.literal_whitespace:
             # Insert a blank before the newline, to avoid
             # ! LaTeX Error: There's no line here to end.
-            text = text.replace("\n", '~\\\\\n').replace(" ", "~")
+            text = text.replace(u'\n', u'~\\\\\n').replace(u' ', u'~')
         return text
 
     def visit_Text(self, node):
@@ -1101,4 +1081,4 @@
         self.body.append('\n')
 
     def unknown_visit(self, node):
-        raise NotImplementedError("Unknown node: " + node.__class__.__name__)
+        raise NotImplementedError('Unknown node: ' + node.__class__.__name__)

Modified: doctools/trunk/tests/test_markup.py
==============================================================================
--- doctools/trunk/tests/test_markup.py	(original)
+++ doctools/trunk/tests/test_markup.py	Sun Aug 10 18:59:27 2008
@@ -63,10 +63,14 @@
         latex_translator.first_document = -1 # don't write \begin{document}
         document.walkabout(latex_translator)
         latex_translated = ''.join(latex_translator.body).strip()
-        assert re.match(latex_expected, latex_translated), 'from ' + rst
+        assert re.match(latex_expected, latex_translated), 'from ' + repr(rst)
 
 def verify(rst, html_expected, latex_expected):
-    verify_re(rst, re.escape(html_expected) + '$', re.escape(latex_expected) + '$')
+    if html_expected:
+        html_expected = re.escape(html_expected) + '$'
+    if latex_expected:
+        latex_expected = re.escape(latex_expected) + '$'
+    verify_re(rst, html_expected, latex_expected)
 
 
 def test_inline():
@@ -85,7 +89,7 @@
     # interpolation of arrows in menuselection
     verify(':menuselection:`a --> b`',
            u'<p><em>a \N{TRIANGULAR BULLET} b</em></p>',
-           '\\emph{a $\\rightarrow$ b}')
+           '\\emph{a \\(\\rightarrow\\) b}')
 
     # non-interpolation of dashes in option role
     verify_re(':option:`--with-option`',
@@ -99,3 +103,12 @@
            '<p><tt class="docutils literal"><span class="pre">'
            '&quot;John&quot;</span></tt></p>',
            '\\code{"John"}')
+
+def test_latex_escaping():
+    # correct escaping in normal mode
+    verify(u'Γ\\\\∞$', None, ur'\(\Gamma\)\textbackslash{}\(\infty\)\$')
+    # in verbatim code fragments
+    verify(u'::\n\n @Γ\\∞$[]', None,
+           u'\\begin{Verbatim}[commandchars=@\\[\\]]\n'
+           u'@at[]@(@Gamma@)\\@(@infty@)@$@lb[]@rb[]\n'
+           u'\\end{Verbatim}')


More information about the Python-checkins mailing list