[Python-checkins] r59408 - in doctools/trunk: TODO sphinx/addnodes.py sphinx/builder.py sphinx/directives.py sphinx/environment.py sphinx/htmlwriter.py sphinx/latexwriter.py sphinx/util/__init__.py

georg.brandl python-checkins at python.org
Fri Dec 7 21:27:52 CET 2007


Author: georg.brandl
Date: Fri Dec  7 21:27:52 2007
New Revision: 59408

Modified:
   doctools/trunk/TODO
   doctools/trunk/sphinx/addnodes.py
   doctools/trunk/sphinx/builder.py
   doctools/trunk/sphinx/directives.py
   doctools/trunk/sphinx/environment.py
   doctools/trunk/sphinx/htmlwriter.py
   doctools/trunk/sphinx/latexwriter.py
   doctools/trunk/sphinx/util/__init__.py
Log:
- rename webify/unwebify to canonical/os_path
- build all documents correctly, including howtos
- pack ACKS content into an .. acks directive, to
  make it possible to render it into a comma-separated
  list in latex


Modified: doctools/trunk/TODO
==============================================================================
--- doctools/trunk/TODO	(original)
+++ doctools/trunk/TODO	Fri Dec  7 21:27:52 2007
@@ -10,6 +10,7 @@
 - "seealso" links to external examples, see http://svn.python.org/projects/sandbox/trunk/seealso/ and http://effbot.org/zone/idea-seealso.htm
 - write a "printable" builder (export to latex, most probably)
 - "often used" combo box in sidebar
+- link to keywords
 - source file cross-references?
 
 Web App

Modified: doctools/trunk/sphinx/addnodes.py
==============================================================================
--- doctools/trunk/sphinx/addnodes.py	(original)
+++ doctools/trunk/sphinx/addnodes.py	Fri Dec  7 21:27:52 2007
@@ -52,6 +52,9 @@
 # compact paragraph -- never makes a <p>
 class compact_paragraph(nodes.paragraph): pass
 
+# for the ACKS list
+class acks(nodes.Element): pass
+
 # sets the highlighting language for literal blocks
 class highlightlang(nodes.Element): pass
 
@@ -67,4 +70,4 @@
       desc_classname desc_name desc_parameterlist desc_parameter desc_optional
       centered versionmodified seealso productionlist production toctree
       pending_xref compact_paragraph highlightlang literal_emphasis
-      glossary""".split())
+      glossary acks""".split())

Modified: doctools/trunk/sphinx/builder.py
==============================================================================
--- doctools/trunk/sphinx/builder.py	(original)
+++ doctools/trunk/sphinx/builder.py	Fri Dec  7 21:27:52 2007
@@ -26,9 +26,8 @@
 from docutils.readers import doctree
 from docutils.frontend import OptionParser
 
-from .util import (get_matching_files, attrdict, status_iterator,
-                   ensuredir, get_category, relative_uri,
-                   webify_filepath, unwebify_filepath)
+from .util import (get_matching_files, attrdict, status_iterator, ensuredir,
+                   get_category, relative_uri, os_path, SEP)
 from .htmlhelp import build_hhx
 from .patchlevel import get_version_info, get_sys_version_info
 from .htmlwriter import HTMLWriter
@@ -491,12 +490,12 @@
             self.srcdir, '*.rst', exclude=set(self.config.get('unused_files', ()))):
             try:
                 targetmtime = path.getmtime(path.join(self.outdir,
-                                                      unwebify_filepath(filename)[:-4] + '.html'))
+                                                      os_path(filename)[:-4] + '.html'))
             except:
                 targetmtime = 0
             if filename not in self.env.all_files:
                 yield filename
-            elif path.getmtime(path.join(self.srcdir, unwebify_filepath(filename))) > targetmtime:
+            elif path.getmtime(path.join(self.srcdir, os_path(filename))) > targetmtime:
                 yield filename
 
 
@@ -521,7 +520,7 @@
         ctx = self.globalcontext.copy()
         ctx.update(context)
         output = self.templates[templatename].render(ctx)
-        outfilename = path.join(self.outdir, unwebify_filepath(filename)[:-4] + '.html')
+        outfilename = path.join(self.outdir, os_path(filename)[:-4] + '.html')
         ensuredir(path.dirname(outfilename)) # normally different from self.outdir
         try:
             with codecs.open(outfilename, 'w', 'utf-8') as fp:
@@ -530,8 +529,8 @@
             print >>self.warning_stream, "Error writing file %s: %s" % (outfilename, err)
         if self.copysource and context.get('sourcename'):
             # copy the source file for the "show source" link
-            shutil.copyfile(path.join(self.srcdir, unwebify_filepath(filename)),
-                            path.join(self.outdir, context['sourcename']))
+            shutil.copyfile(path.join(self.srcdir, os_path(filename)),
+                            path.join(self.outdir, os_path(context['sourcename'])))
 
     def handle_finish(self):
         self.msg('dumping search index...')
@@ -554,19 +553,20 @@
         for filename in get_matching_files(
             self.srcdir, '*.rst', exclude=set(self.config.get('unused_files', ()))):
             try:
-                targetmtime = path.getmtime(path.join(self.outdir,
-                                                      unwebify_filepath(filename)[:-4] + '.fpickle'))
+                targetmtime = path.getmtime(
+                    path.join(self.outdir, os_path(filename)[:-4] + '.fpickle'))
             except:
                 targetmtime = 0
-            if path.getmtime(path.join(self.srcdir, unwebify_filepath(filename))) > targetmtime:
+            if path.getmtime(path.join(self.srcdir,
+                                       os_path(filename))) > targetmtime:
                 yield filename
 
     def get_target_uri(self, source_filename):
         if source_filename == 'index.rst':
             return ''
-        if source_filename.endswith('/index.rst'):
-            return source_filename[:-9] # up to /
-        return source_filename[:-4] + '/'
+        if source_filename.endswith(SEP+'index.rst'):
+            return source_filename[:-9] # up to sep
+        return source_filename[:-4] + SEP
 
     def load_indexer(self, filenames):
         try:
@@ -585,7 +585,7 @@
                 self.indexer.feed(filename, category, title, doctree)
 
     def handle_file(self, filename, context, templatename='page'):
-        outfilename = path.join(self.outdir, unwebify_filepath(filename)[:-4] + '.fpickle')
+        outfilename = path.join(self.outdir, os_path(filename)[:-4] + '.fpickle')
         ensuredir(path.dirname(outfilename))
         context.pop('pathto', None) # can't be pickled
         with file(outfilename, 'wb') as fp:
@@ -593,9 +593,9 @@
 
         # if there is a source file, copy the source file for the "show source" link
         if context.get('sourcename'):
-            source_name = path.join(self.outdir, 'sources', context['sourcename'])
+            source_name = path.join(self.outdir, 'sources', os_path(context['sourcename']))
             ensuredir(path.dirname(source_name))
-            shutil.copyfile(path.join(self.srcdir, unwebify_filepath(filename)), source_name)
+            shutil.copyfile(path.join(self.srcdir, os_path(filename)), source_name)
 
     def handle_finish(self):
         # dump the global context
@@ -659,6 +659,17 @@
         else:
             return ''
 
+    def get_document_data(self):
+        for toplevel in ["c-api", "distutils", "documenting", "extending",
+                        "install", "reference", "tutorial", "using", "library"]:
+            yield (toplevel + SEP + 'index.rst', toplevel+'.tex', 'manual')
+        yield ('whatsnew' + SEP + self.config['version'] + '.rst',
+               'whatsnew.tex', 'howto')
+        for howto in [fn for fn in self.env.all_files
+                      if fn.startswith('howto'+SEP)
+                         and not fn.endswith('index.rst')]:
+            yield (howto, 'howto-'+howto[6:-4]+'.tex', 'howto')
+
     def write(self, filenames):
         # "filenames" is ignored here...
 
@@ -672,26 +683,24 @@
             defaults=self.env.settings,
             components=(docwriter,)).get_default_values()
 
-        # XXX get names of toplevels automatically?
-        for docname in ["library"]:#, "distutils", "documenting", "extending",
-                        #"howto", "install", "library", "reference",
-                        #"tutorial", "using"]:
-            # XXX whatsnew missing
+        for sourcename, targetname, docclass in self.get_document_data():
             destination = FileOutput(
-                destination_path=path.join(self.outdir, docname+".tex"),
+                destination_path=path.join(self.outdir, targetname),
                 encoding='utf-8')
-            doctree = self.assemble_doctree(path.join(docname, "index.rst"))
-            doctree.extend(specials)
+            print "processing", targetname + "...",
+            doctree = self.assemble_doctree(sourcename,
+                                            specials=(docclass == 'manual') and specials or [])
             print "writing...",
             doctree.settings = docsettings
-            doctree.settings.filename = docname
-            doctree.settings.docclass = 'manual'  # XXX howto for whatsnew
+            doctree.settings.filename = sourcename
+            doctree.settings.docclass = docclass
             output = docwriter.write(doctree, destination)
             print "done"
 
-    def assemble_doctree(self, indexfile):
-        self.filenames = [indexfile]
-        print "processing", indexfile
+    def assemble_doctree(self, indexfile, specials):
+        self.filenames = set([indexfile, 'glossary.rst', 'about.rst',
+                              'license.rst', 'copyright.rst'])
+        print green(indexfile),
         def process_tree(tree):
             #tree = tree.deepcopy() XXX
             for toctreenode in tree.traverse(addnodes.toctree):
@@ -701,7 +710,7 @@
                     try:
                         print green(includefile),
                         subtree = process_tree(self.env.get_doctree(includefile))
-                        self.filenames.append(includefile)
+                        self.filenames.add(includefile)
                     except:
                         print >>self.warning_stream, 'WARNING: %s: toctree contains ' \
                               'ref to nonexisting file %r' % (filename, includefile)
@@ -710,6 +719,7 @@
                 toctreenode.parent.replace(toctreenode, newnodes)
             return tree
         largetree = process_tree(self.env.get_doctree(indexfile))
+        largetree.extend(specials)
         print
         print "resolving references..."
         self.env.resolve_references(largetree, indexfile, self)

Modified: doctools/trunk/sphinx/directives.py
==============================================================================
--- doctools/trunk/sphinx/directives.py	(original)
+++ doctools/trunk/sphinx/directives.py	Fri Dec  7 21:27:52 2007
@@ -12,6 +12,7 @@
 
 import re
 import string
+import posixpath
 from os import path
 
 from docutils import nodes
@@ -19,7 +20,6 @@
 from docutils.parsers.rst.directives import admonitions
 
 from . import addnodes
-from .util import webify_filepath, unwebify_filepath
 
 # ------ index markup --------------------------------------------------------------
 
@@ -552,13 +552,12 @@
 def toctree_directive(name, arguments, options, content, lineno,
                       content_offset, block_text, state, state_machine):
     env = state.document.settings.env
-    dirname = path.dirname(env.filename)
+    dirname = posixpath.dirname(env.filename)
 
     subnode = addnodes.toctree()
     includefiles = filter(None, content)
     # absolutize filenames
-    includefiles = [webify_filepath(path.normpath(path.join (dirname, x))) for x in includefiles]
-    #~ includefiles = map(lambda x: path.normpath(path.join(dirname, x)), includefiles)
+    includefiles = [posixpath.normpath(posixpath.join(dirname, x)) for x in includefiles]
     subnode['includefiles'] = includefiles
     subnode['maxdepth'] = options.get('maxdepth', -1)
     return [subnode]
@@ -603,16 +602,16 @@
         return [state.document.reporter.warning('File insertion disabled', line=lineno)]
     env = state.document.settings.env
     fn = arguments[0]
-    source_dir = webify_filepath(path.dirname(path.abspath(state_machine.input_lines.source(
-        lineno - state_machine.input_offset - 1))))
-    fn = webify_filepath(path.normpath(path.join(source_dir, fn)))
+    source_dir = path.dirname(path.abspath(state_machine.input_lines.source(
+        lineno - state_machine.input_offset - 1)))
+    fn = path.normpath(path.join(source_dir, fn))
 
     try:
         with open(fn) as f:
             text = f.read()
     except (IOError, OSError):
-        retnode = state.document.reporter.warning('Include file %r not found' %
-                                                  arguments[0], line=lineno)
+        retnode = state.document.reporter.warning(
+            'Include file %r not found or reading it failed' % arguments[0], line=lineno)
     else:
         retnode = nodes.literal_block(text, text, source=fn)
         retnode.line = 1
@@ -654,3 +653,19 @@
 glossary_directive.content = 1
 glossary_directive.arguments = (0, 0, 0)
 directives.register_directive('glossary', glossary_directive)
+
+
+# ------ acks directive -------------------------------------------------------------
+
+def acks_directive(name, arguments, options, content, lineno,
+                   content_offset, block_text, state, state_machine):
+    node = addnodes.acks()
+    state.nested_parse(content, content_offset, node)
+    if len(node.children) != 1 or not isinstance(node.children[0], nodes.bullet_list):
+        return [state.document.reporter.warning('.. acks content is not a list',
+                                                line=lineno)]
+    return [node]
+
+acks_directive.content = 1
+acks_directive.arguments = (0, 0, 0)
+directives.register_directive('acks', acks_directive)

Modified: doctools/trunk/sphinx/environment.py
==============================================================================
--- doctools/trunk/sphinx/environment.py	(original)
+++ doctools/trunk/sphinx/environment.py	Fri Dec  7 21:27:52 2007
@@ -38,7 +38,7 @@
     Body.enum.converters['upperroman'] = lambda x: None
 
 from . import addnodes
-from .util import get_matching_files, unwebify_filepath, WEB_SEP
+from .util import get_matching_files, os_path, SEP
 from .refcounting import Refcounts
 
 default_settings = {
@@ -283,11 +283,11 @@
                 else:
                     # if the doctree file is not there, rebuild
                     if not path.isfile(path.join(self.doctreedir,
-                                                 unwebify_filepath(filename)[:-3] + 'doctree')):
+                                                 os_path(filename)[:-3] + 'doctree')):
                         changed.append(filename)
                         continue
                     mtime, md5 = self.all_files[filename]
-                    newmtime = path.getmtime(path.join(self.srcdir, unwebify_filepath(filename)))
+                    newmtime = path.getmtime(path.join(self.srcdir, os_path(filename)))
                     if newmtime == mtime:
                         continue
                     # check the MD5
@@ -302,8 +302,8 @@
         """
         (Re-)read all files new or changed since last update.
         Yields a summary and then filenames as it processes them.
-        Store all environment filenames as webified (ie using "/"
-        as a separator in place of os.path.sep).
+        Store all environment filenames in the canonical format
+        (ie using SEP as a separator in place of os.path.sep).
         """
         added, changed, removed = self.get_outdated_files(config)
         msg = '%s added, %s changed, %s removed' % (len(added), len(changed),
@@ -336,7 +336,7 @@
         self.clear_file(filename)
 
         if src_path is None:
-            src_path = path.join(self.srcdir, unwebify_filepath(filename))
+            src_path = path.join(self.srcdir, os_path(filename))
 
         self.filename = filename
         doctree = publish_doctree(None, src_path, FileInput,
@@ -367,7 +367,7 @@
 
         if save_parsed:
             # save the parsed doctree
-            doctree_filename = path.join(self.doctreedir, unwebify_filepath(filename)[:-3] + 'doctree')
+            doctree_filename = path.join(self.doctreedir, os_path(filename)[:-3] + 'doctree')
             dirname = path.dirname(doctree_filename)
             if not path.isdir(dirname):
                 os.makedirs(dirname)
@@ -523,7 +523,7 @@
 
     def get_doctree(self, filename):
         """Read the doctree for a file from the pickle and return it."""
-        doctree_filename = path.join(self.doctreedir, unwebify_filepath(filename)[:-3] + 'doctree')
+        doctree_filename = path.join(self.doctreedir, os_path(filename)[:-3] + 'doctree')
         with file(doctree_filename, 'rb') as f:
             doctree = pickle.load(f)
         doctree.reporter = Reporter(filename, 2, 4, stream=self.warning_stream)
@@ -597,12 +597,16 @@
                               '%s: undefined label: %s' % (docfilename, target)
                     else:
                         newnode = nodes.reference('', '')
+                        innernode = nodes.emphasis(sectname, sectname)
                         if filename == docfilename:
                             newnode['refid'] = labelid
                         else:
+                            # in case the following calls raises NoUri...
+                            # else the final node will contain a label name
+                            contnode = innernode
                             newnode['refuri'] = builder.get_relative_uri(
                                 docfilename, filename) + '#' + labelid
-                        newnode.append(nodes.emphasis(sectname, sectname))
+                        newnode.append(innernode)
                 elif typ in ('token', 'term', 'envvar', 'option'):
                     filename, labelid = self.reftargets.get((typ, target), ('', ''))
                     if not filename:
@@ -874,6 +878,6 @@
         filename. This also resolves the special `index.rst` files. If the file
         does not exist the return value will be `None`.
         """
-        for rstname in filename + '.rst', filename + WEB_SEP + 'index.rst':
+        for rstname in filename + '.rst', filename + SEP + 'index.rst':
             if rstname in self.all_files:
                 return rstname

Modified: doctools/trunk/sphinx/htmlwriter.py
==============================================================================
--- doctools/trunk/sphinx/htmlwriter.py	(original)
+++ doctools/trunk/sphinx/htmlwriter.py	Fri Dec  7 21:27:52 2007
@@ -227,6 +227,11 @@
         def depart_glossary(self, node):
             pass
 
+        def visit_acks(self, node):
+            pass
+        def depart_acks(self, node):
+            pass
+
         # these are only handled specially in the SmartyPantsHTMLTranslator
         def visit_literal_emphasis(self, node):
             return self.visit_emphasis(node)

Modified: doctools/trunk/sphinx/latexwriter.py
==============================================================================
--- doctools/trunk/sphinx/latexwriter.py	(original)
+++ doctools/trunk/sphinx/latexwriter.py	Fri Dec  7 21:27:52 2007
@@ -24,6 +24,7 @@
 
 HEADER = r'''%% Generated by Sphinx.
 \documentclass[%(papersize)s,%(pointsize)s]{%(docclass)s}
+\usepackage[utf8]{inputenc}
 \usepackage[colorlinks]{hyperref}
 \title{%(title)s}
 \date{%(date)s}
@@ -91,7 +92,8 @@
     def __init__(self, document, config):
         nodes.NodeVisitor.__init__(self, document)
         self.body = []
-        self.options = {'docclass': document.settings.docclass,
+        docclass = document.settings.docclass
+        self.options = {'docclass': docclass,
                         'papersize': 'a4paper', # XXX
                         'pointsize': '12pt',
                         'filename': document.settings.filename,
@@ -103,6 +105,7 @@
         self.descstack = []
         self.highlightlang = 'python'
         self.written_ids = set()
+        self.top_sectionlevel = 0 if docclass == 'manual' else 1
         # flags
         self.verbatim = None
         self.in_title = 0
@@ -123,7 +126,7 @@
         elif self.first_document == 0:
             self.body.append('\n\\appendix\n')
             self.first_document = -1
-        self.sectionlevel = 0
+        self.sectionlevel = self.top_sectionlevel
     def depart_document(self, node):
         pass
 
@@ -146,6 +149,12 @@
     def depart_section(self, node):
         self.sectionlevel -= 1
 
+    def visit_topic(self, node):
+        raise nodes.SkipNode # XXX
+
+    def visit_sidebar(self, node):
+        raise nodes.SkipNode # XXX
+
     def visit_glossary(self, node):
         raise nodes.SkipNode # XXX
 
@@ -365,6 +374,14 @@
         else:
             self.body.append('}')
 
+    def visit_acks(self, node):
+        # this is a list in the source, but should be rendered as a
+        # comma-separated list here
+        self.body.append('\n\n')
+        self.body.append(', '.join(n.astext() for n in node.children[0].children))
+        self.body.append('\n\n')
+        raise nodes.SkipNode
+
     def visit_bullet_list(self, node):
         self.body.append('\\begin{itemize}\n' )
     def depart_bullet_list(self, node):
@@ -502,7 +519,9 @@
         self.body.append('}')
 
     def visit_title_reference(self, node):
-        raise RuntimeError("XXX title reference node found")
+        self.body.append(r'\emph{')
+    def depart_title_reference(self, node):
+        self.body.append('}')
 
     def visit_literal(self, node):
         content = self.encode(node.astext().strip())
@@ -519,13 +538,13 @@
         raise nodes.SkipNode
 
     def visit_literal_block(self, node):
-        #self.body.append('\n\\begin{Verbatim}\n')
         self.verbatim = ''
     def depart_literal_block(self, node):
-        #self.body.append('\n\\end{Verbatim}\n')
-        self.body.append('\n' + highlighting.highlight_block(self.verbatim,
-                                                             self.highlightlang,
-                                                             'latex'))
+        hlcode = highlighting.highlight_block(self.verbatim.rstrip('\n'),
+                                              self.highlightlang, 'latex')
+        # workaround for Pygments bug
+        hlcode = hlcode.replace('\n\\end{Verbatim}', '\\end{Verbatim}')
+        self.body.append('\n' + hlcode)
         self.verbatim = None
     visit_doctest_block = visit_literal_block
     depart_doctest_block = depart_literal_block
@@ -600,7 +619,7 @@
 
     def visit_Text(self, node):
         if self.verbatim is not None:
-            self.verbatim += node.astext().replace('@', '@@')
+            self.verbatim += node.astext()
         else:
             self.body.append(self.encode(node.astext()))
     def depart_Text(self, node):

Modified: doctools/trunk/sphinx/util/__init__.py
==============================================================================
--- doctools/trunk/sphinx/util/__init__.py	(original)
+++ doctools/trunk/sphinx/util/__init__.py	Fri Dec  7 21:27:52 2007
@@ -15,35 +15,31 @@
 from os import path
 
 
+# SEP separates path elements in the canonical file names
 #
-# Define WEB_SEP as a manifest constant, not
-# so much because we expect it to change in
-# the future as to avoid the suspicion that
-# a stray "/" in the code is a hangover from
-# more *nix-oriented origins.
-#
-WEB_SEP = "/"
+# Define SEP as a manifest constant, not so much because we expect it to change
+# in the future as to avoid the suspicion that a stray "/" in the code is a
+# hangover from more *nix-oriented origins.
+SEP = "/"
 
+def canonical_path(ospath):
+    return ospath.replace(os.path.sep, SEP)
 
-def webify_filepath(filepath):
-    return filepath.replace(os.path.sep, WEB_SEP)
-    
-    
-def unwebify_filepath(webpath):
-    return webpath.replace(WEB_SEP, os.path.sep)
+def os_path(canpath):
+    return canpath.replace(SEP, os.path.sep)
 
 
 def relative_uri(base, to):
     """Return a relative URL from ``base`` to ``to``."""
-    b2 = base.split(WEB_SEP)
-    t2 = to.split(WEB_SEP)
+    b2 = base.split(SEP)
+    t2 = to.split(SEP)
     # remove common segments
     for x, y in zip(b2, t2):
         if x != y:
             break
         b2.pop(0)
         t2.pop(0)
-    return ('..' + WEB_SEP) * (len(b2)-1) + WEB_SEP.join(t2)
+    return ('..' + SEP) * (len(b2)-1) + SEP.join(t2)
 
 
 def ensuredir(path):
@@ -78,12 +74,12 @@
             qualified_name = path.join(root[dirlen:], sfile)
             if qualified_name in exclude:
                 continue
-            yield webify_filepath(qualified_name)
+            yield canonical_path(qualified_name)
 
 
 def get_category(filename):
     """Get the "category" part of a RST filename."""
-    parts = filename.split(WEB_SEP, 1)
+    parts = filename.split(SEP, 1)
     if len(parts) < 2:
         return
     return parts[0]


More information about the Python-checkins mailing list