[Python-checkins] r59309 - in doctools/trunk/sphinx: builder.py directives.py environment.py htmlwriter.py util/__init__.py writer.py

georg.brandl python-checkins at python.org
Mon Dec 3 23:38:25 CET 2007


Author: georg.brandl
Date: Mon Dec  3 23:38:25 2007
New Revision: 59309

Added:
   doctools/trunk/sphinx/htmlwriter.py
      - copied, changed from r58585, doctools/trunk/sphinx/writer.py
Removed:
   doctools/trunk/sphinx/writer.py
Modified:
   doctools/trunk/sphinx/builder.py
   doctools/trunk/sphinx/directives.py
   doctools/trunk/sphinx/environment.py
   doctools/trunk/sphinx/util/__init__.py
Log:
Apply Tim Golden's patch from #1520, which resolves confusion between
file paths and relative URIs so that building on Windows is flawlessly
possible.


Modified: doctools/trunk/sphinx/builder.py
==============================================================================
--- doctools/trunk/sphinx/builder.py	(original)
+++ doctools/trunk/sphinx/builder.py	Mon Dec  3 23:38:25 2007
@@ -27,13 +27,15 @@
 from docutils.frontend import OptionParser
 
 from .util import (get_matching_files, attrdict, status_iterator,
-                   ensuredir, get_category, relative_uri)
-from .writer import HTMLWriter
-from .util.console import bold, purple, green
+                   ensuredir, get_category, relative_uri,
+                   webify_filepath, unwebify_filepath)
 from .htmlhelp import build_hhx
 from .patchlevel import get_version_info, get_sys_version_info
+from .htmlwriter import HTMLWriter
+#from .latexwriter import LaTeXWriter
 from .environment import BuildEnvironment
 from .highlighting import pygments, get_stylesheet
+from .util.console import bold, purple, green
 
 # side effect: registers roles and directives
 from . import roles
@@ -234,7 +236,7 @@
             # build all
             filenames_set = set(self.env.all_files)
 
-        self.prepare_writing(filenames)
+        self.prepare_writing(filenames_set)
 
         # write target files
         with collect_env_warnings(self):
@@ -483,12 +485,12 @@
             self.srcdir, '*.rst', exclude=set(self.config.get('unused_files', ()))):
             try:
                 targetmtime = path.getmtime(path.join(self.outdir,
-                                                      filename[:-4] + '.html'))
+                                                      unwebify_filepath(filename)[:-4] + '.html'))
             except:
                 targetmtime = 0
             if filename not in self.env.all_files:
                 yield filename
-            elif path.getmtime(path.join(self.srcdir, filename)) > targetmtime:
+            elif path.getmtime(path.join(self.srcdir, unwebify_filepath(filename))) > targetmtime:
                 yield filename
 
 
@@ -513,7 +515,7 @@
         ctx = self.globalcontext.copy()
         ctx.update(context)
         output = self.templates[templatename].render(ctx)
-        outfilename = path.join(self.outdir, filename[:-4] + '.html')
+        outfilename = path.join(self.outdir, unwebify_filepath(filename)[:-4] + '.html')
         ensuredir(path.dirname(outfilename)) # normally different from self.outdir
         try:
             with codecs.open(outfilename, 'w', 'utf-8') as fp:
@@ -522,7 +524,7 @@
             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, filename),
+            shutil.copyfile(path.join(self.srcdir, unwebify_filepath(filename)),
                             path.join(self.outdir, context['sourcename']))
 
     def handle_finish(self):
@@ -547,10 +549,10 @@
             self.srcdir, '*.rst', exclude=set(self.config.get('unused_files', ()))):
             try:
                 targetmtime = path.getmtime(path.join(self.outdir,
-                                                      filename[:-4] + '.fpickle'))
+                                                      unwebify_filepath(filename)[:-4] + '.fpickle'))
             except:
                 targetmtime = 0
-            if path.getmtime(path.join(self.srcdir, filename)) > targetmtime:
+            if path.getmtime(path.join(self.srcdir, unwebify_filepath(filename))) > targetmtime:
                 yield filename
 
     def get_target_uri(self, source_filename):
@@ -577,7 +579,7 @@
                 self.indexer.feed(filename, category, title, doctree)
 
     def handle_file(self, filename, context, templatename='page'):
-        outfilename = path.join(self.outdir, filename[:-4] + '.fpickle')
+        outfilename = path.join(self.outdir, unwebify_filepath(filename)[:-4] + '.fpickle')
         ensuredir(path.dirname(outfilename))
         context.pop('pathto', None) # can't be pickled
         with file(outfilename, 'wb') as fp:
@@ -587,7 +589,7 @@
         if context.get('sourcename'):
             source_name = path.join(self.outdir, 'sources', context['sourcename'])
             ensuredir(path.dirname(source_name))
-            shutil.copyfile(path.join(self.srcdir, filename), source_name)
+            shutil.copyfile(path.join(self.srcdir, unwebify_filepath(filename)), source_name)
 
     def handle_finish(self):
         # dump the global context
@@ -632,8 +634,43 @@
         build_hhx(self, self.outdir, self.options.get('outname') or 'pydoc')
 
 
+class LaTeXBuilder(Builder):
+    """
+    Builds LaTeX output to create PDF.
+    """
+    name = 'latex'
+
+    def init(self):
+        pass
+
+    def get_outdated_files(self):
+        # always rebuild everything for now
+        return self.env.all_files
+
+    def get_target_uri(self, source_filename):
+        # XXX: returns nothing for now
+        return ''
+
+    def prepare_writing(self, filenames):
+        self.docwriter = LaTeXWriter(self.config, self.name)
+        self.docsettings = OptionParser(
+            defaults=self.env.settings,
+            components=(self.docwriter,)).get_default_values()
+        
+
+    def write_file(self, filename, doctree):
+        destination = StringOutput(encoding='utf-8')
+        doctree.settings = self.docsettings
+        output = self.docwriter.write(doctree, destination)
+        print output
+
+    def finish(self):
+        pass
+
+
 builders = {
     'html': StandaloneHTMLBuilder,
     'web': WebHTMLBuilder,
     'htmlhelp': HTMLHelpBuilder,
+#    'latex': LaTeXBuilder,
 }

Modified: doctools/trunk/sphinx/directives.py
==============================================================================
--- doctools/trunk/sphinx/directives.py	(original)
+++ doctools/trunk/sphinx/directives.py	Mon Dec  3 23:38:25 2007
@@ -19,6 +19,7 @@
 from docutils.parsers.rst.directives import admonitions
 
 from . import addnodes
+from .util import webify_filepath, unwebify_filepath
 
 # ------ index markup --------------------------------------------------------------
 
@@ -554,7 +555,8 @@
     subnode = addnodes.toctree()
     includefiles = filter(None, content)
     # absolutize filenames
-    includefiles = map(lambda x: path.normpath(path.join(dirname, x)), includefiles)
+    includefiles = [webify_filepath(path.normpath(path.join (dirname, x))) for x in includefiles]
+    #~ includefiles = map(lambda x: path.normpath(path.join(dirname, x)), includefiles)
     subnode['includefiles'] = includefiles
     subnode['maxdepth'] = options.get('maxdepth', -1)
     return [subnode]
@@ -599,9 +601,9 @@
         return [state.document.reporter.warning('File insertion disabled', line=lineno)]
     env = state.document.settings.env
     fn = arguments[0]
-    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))
+    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)))
 
     try:
         with open(fn) as f:

Modified: doctools/trunk/sphinx/environment.py
==============================================================================
--- doctools/trunk/sphinx/environment.py	(original)
+++ doctools/trunk/sphinx/environment.py	Mon Dec  3 23:38:25 2007
@@ -38,7 +38,7 @@
     Body.enum.converters['upperroman'] = lambda x: None
 
 from . import addnodes
-from .util import get_matching_files
+from .util import get_matching_files, unwebify_filepath, WEB_SEP
 from .refcounting import Refcounts
 
 default_settings = {
@@ -278,11 +278,11 @@
                 else:
                     # if the doctree file is not there, rebuild
                     if not path.isfile(path.join(self.doctreedir,
-                                                 filename[:-3] + 'doctree')):
+                                                 unwebify_filepath(filename)[:-3] + 'doctree')):
                         changed.append(filename)
                         continue
                     mtime, md5 = self.all_files[filename]
-                    newmtime = path.getmtime(path.join(self.srcdir, filename))
+                    newmtime = path.getmtime(path.join(self.srcdir, unwebify_filepath(filename)))
                     if newmtime == mtime:
                         continue
                     # check the MD5
@@ -297,6 +297,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).
         """
         added, changed, removed = self.get_outdated_files(config)
         msg = '%s added, %s changed, %s removed' % (len(added), len(changed),
@@ -329,7 +331,7 @@
         self.clear_file(filename)
 
         if src_path is None:
-            src_path = path.join(self.srcdir, filename)
+            src_path = path.join(self.srcdir, unwebify_filepath(filename))
 
         self.filename = filename
         doctree = publish_doctree(None, src_path, FileInput,
@@ -360,7 +362,7 @@
 
         if save_parsed:
             # save the parsed doctree
-            doctree_filename = path.join(self.doctreedir, filename[:-3] + 'doctree')
+            doctree_filename = path.join(self.doctreedir, unwebify_filepath(filename)[:-3] + 'doctree')
             dirname = path.dirname(doctree_filename)
             if not path.isdir(dirname):
                 os.makedirs(dirname)
@@ -516,7 +518,7 @@
 
     def get_doctree(self, filename):
         """Read the doctree for a file from the pickle and return it."""
-        doctree_filename = path.join(self.doctreedir, filename[:-3] + 'doctree')
+        doctree_filename = path.join(self.doctreedir, unwebify_filepath(filename)[:-3] + 'doctree')
         with file(doctree_filename, 'rb') as f:
             doctree = pickle.load(f)
         doctree.reporter = Reporter(filename, 2, 4, stream=self.warning_stream)
@@ -862,6 +864,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 + path.sep + 'index.rst':
+        for rstname in filename + '.rst', filename + WEB_SEP + 'index.rst':
             if rstname in self.all_files:
                 return rstname

Copied: doctools/trunk/sphinx/htmlwriter.py (from r58585, doctools/trunk/sphinx/writer.py)
==============================================================================
--- doctools/trunk/sphinx/writer.py	(original)
+++ doctools/trunk/sphinx/htmlwriter.py	Mon Dec  3 23:38:25 2007
@@ -1,7 +1,7 @@
 # -*- coding: utf-8 -*-
 """
-    sphinx.writer
-    ~~~~~~~~~~~~~
+    sphinx.htmlwriter
+    ~~~~~~~~~~~~~~~~~
 
     docutils writers handling Sphinx' custom nodes.
 

Modified: doctools/trunk/sphinx/util/__init__.py
==============================================================================
--- doctools/trunk/sphinx/util/__init__.py	(original)
+++ doctools/trunk/sphinx/util/__init__.py	Mon Dec  3 23:38:25 2007
@@ -15,17 +15,35 @@
 from os import path
 
 
+#
+# 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 = "/"
+
+
+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 relative_uri(base, to):
     """Return a relative URL from ``base`` to ``to``."""
-    b2 = base.split('/')
-    t2 = to.split('/')
+    b2 = base.split(WEB_SEP)
+    t2 = to.split(WEB_SEP)
     # remove common segments
     for x, y in zip(b2, t2):
         if x != y:
             break
         b2.pop(0)
         t2.pop(0)
-    return '../' * (len(b2)-1) + '/'.join(t2)
+    return ('..' + WEB_SEP) * (len(b2)-1) + WEB_SEP.join(t2)
 
 
 def ensuredir(path):
@@ -60,12 +78,12 @@
             qualified_name = path.join(root[dirlen:], sfile)
             if qualified_name in exclude:
                 continue
-            yield qualified_name
+            yield webify_filepath(qualified_name)
 
 
 def get_category(filename):
     """Get the "category" part of a RST filename."""
-    parts = filename.split('/', 1)
+    parts = filename.split(WEB_SEP, 1)
     if len(parts) < 2:
         return
     return parts[0]

Deleted: /doctools/trunk/sphinx/writer.py
==============================================================================
--- /doctools/trunk/sphinx/writer.py	Mon Dec  3 23:38:25 2007
+++ (empty file)
@@ -1,292 +0,0 @@
-# -*- coding: utf-8 -*-
-"""
-    sphinx.writer
-    ~~~~~~~~~~~~~
-
-    docutils writers handling Sphinx' custom nodes.
-
-    :copyright: 2007 by Georg Brandl.
-    :license: Python license.
-"""
-
-from docutils import nodes
-from docutils.writers.html4css1 import Writer, HTMLTranslator as BaseTranslator
-
-from .util.smartypants import sphinx_smarty_pants
-
-
-class HTMLWriter(Writer):
-    def __init__(self, config, buildername):
-        Writer.__init__(self)
-        self.translator_class = translator_class(config, buildername)
-
-
-version_text = {
-    'deprecated': 'Deprecated in version %s',
-    'versionchanged': 'Changed in version %s',
-    'versionadded': 'New in version %s',
-}
-
-def translator_class(config, buildername):
-    class HTMLTranslator(BaseTranslator):
-        """
-        Our custom HTML translator.
-        """
-
-        def __init__(self, *args, **kwds):
-            self.no_smarty = 0
-            BaseTranslator.__init__(self, *args, **kwds)
-            self.highlightlang = 'python'
-            self.language.labels['warning'] = 'Caveat'
-
-        def visit_desc(self, node):
-            self.body.append(self.starttag(node, 'dl', CLASS=node['desctype']))
-        def depart_desc(self, node):
-            self.body.append('</dl>\n\n')
-
-        def visit_desc_signature(self, node):
-            # the id is set automatically
-            self.body.append(self.starttag(node, 'dt'))
-            # anchor for per-desc interactive data
-            if node.parent['desctype'] != 'describe' and node['ids'] and node['first']:
-                self.body.append('<!--#%s#-->' % node['ids'][0])
-            if node.parent['desctype'] in ('class', 'exception'):
-                self.body.append('%s ' % node.parent['desctype'])
-        def depart_desc_signature(self, node):
-            if node['ids'] and buildername != 'htmlhelp':
-                self.body.append(u'<a class="headerlink" href="#%s" ' % node['ids'][0] +
-                                 u'title="Permalink to this definition">\u00B6</a>')
-            self.body.append('</dt>\n')
-
-        def visit_desc_classname(self, node):
-            self.body.append(self.starttag(node, 'tt', '', CLASS='descclassname'))
-        def depart_desc_classname(self, node):
-            self.body.append('</tt>')
-
-        def visit_desc_name(self, node):
-            self.body.append(self.starttag(node, 'tt', '', CLASS='descname'))
-        def depart_desc_name(self, node):
-            self.body.append('</tt>')
-
-        def visit_desc_parameterlist(self, node):
-            self.body.append('<big>(</big>')
-            self.first_param = 1
-        def depart_desc_parameterlist(self, node):
-            self.body.append('<big>)</big>')
-
-        def visit_desc_parameter(self, node):
-            if not self.first_param:
-                self.body.append(', ')
-            else:
-                self.first_param = 0
-            if not node.hasattr('noemph'):
-                self.body.append('<em>')
-        def depart_desc_parameter(self, node):
-            if not node.hasattr('noemph'):
-                self.body.append('</em>')
-
-        def visit_desc_optional(self, node):
-            self.body.append('<span class="optional">[</span>')
-        def depart_desc_optional(self, node):
-            self.body.append('<span class="optional">]</span>')
-
-        def visit_desc_content(self, node):
-            self.body.append(self.starttag(node, 'dd', ''))
-        def depart_desc_content(self, node):
-            self.body.append('</dd>')
-
-        def visit_refcount(self, node):
-            self.body.append(self.starttag(node, 'em', '', CLASS='refcount'))
-        def depart_refcount(self, node):
-            self.body.append('</em>')
-
-        def visit_versionmodified(self, node):
-            self.body.append(self.starttag(node, 'p'))
-            text = version_text[node['type']] % node['version']
-            if len(node):
-                text += ': '
-            else:
-                text += '.'
-            self.body.append('<span class="versionmodified">%s</span>' % text)
-        def depart_versionmodified(self, node):
-            self.body.append('</p>\n')
-
-        # overwritten
-        def visit_reference(self, node):
-            BaseTranslator.visit_reference(self, node)
-            if node.hasattr('reftitle'):
-                # ugly hack to add a title attribute
-                starttag = self.body[-1]
-                if not starttag.startswith('<a '):
-                    return
-                self.body[-1] = '<a title="%s"' % self.attval(node['reftitle']) + \
-                                starttag[2:]
-
-        # overwritten -- we don't want source comments to show up in the HTML
-        def visit_comment(self, node):
-            raise nodes.SkipNode
-
-        # overwritten
-        def visit_admonition(self, node, name=''):
-            self.body.append(self.start_tag_with_title(
-                node, 'div', CLASS=('admonition ' + name)))
-            if name and name != 'seealso':
-                node.insert(0, nodes.title(name, self.language.labels[name]))
-            self.set_first_last(node)
-
-        def visit_seealso(self, node):
-            self.visit_admonition(node, 'seealso')
-        def depart_seealso(self, node):
-            self.depart_admonition(node)
-
-        # overwritten
-        def visit_title(self, node, move_ids=1):
-            # if we have a section we do our own processing in order
-            # to have ids in the hN-tags and not in additional a-tags
-            if isinstance(node.parent, nodes.section):
-                h_level = self.section_level + self.initial_header_level - 1
-                if node.parent.get('ids'):
-                    attrs = {'ids': node.parent['ids']}
-                else:
-                    attrs = {}
-                self.body.append(self.starttag(node, 'h%d' % h_level, '', **attrs))
-                self.context.append('</h%d>\n' % h_level)
-            else:
-                BaseTranslator.visit_title(self, node, move_ids)
-
-        # overwritten
-        def visit_literal_block(self, node):
-            from .highlighting import highlight_block
-            self.body.append(highlight_block(node.rawsource, self.highlightlang))
-            raise nodes.SkipNode
-
-        # overwritten
-        def visit_literal(self, node):
-            if len(node.children) == 1 and \
-                   node.children[0] in ('None', 'True', 'False'):
-                node['classes'].append('xref')
-            BaseTranslator.visit_literal(self, node)
-
-        def visit_productionlist(self, node):
-            self.body.append(self.starttag(node, 'pre'))
-            names = []
-            for production in node:
-                names.append(production['tokenname'])
-            maxlen = max(len(name) for name in names)
-            for production in node:
-                if production['tokenname']:
-                    self.body.append(self.starttag(production, 'strong', ''))
-                    self.body.append(production['tokenname'].ljust(maxlen) +
-                                     '</strong> ::= ')
-                    lastname = production['tokenname']
-                else:
-                    self.body.append('%s     ' % (' '*len(lastname)))
-                production.walkabout(self)
-                self.body.append('\n')
-            self.body.append('</pre>\n')
-            raise nodes.SkipNode
-        def depart_productionlist(self, node):
-            pass
-
-        def visit_production(self, node):
-            pass
-        def depart_production(self, node):
-            pass
-
-        def visit_centered(self, node):
-            self.body.append(self.starttag(node, 'p', CLASS="centered") + '<strong>')
-        def depart_centered(self, node):
-            self.body.append('</strong></p>')
-
-        def visit_compact_paragraph(self, node):
-            pass
-        def depart_compact_paragraph(self, node):
-            pass
-
-        def visit_highlightlang(self, node):
-            self.highlightlang = node['lang']
-        def depart_highlightlang(self, node):
-            pass
-
-        def visit_toctree(self, node):
-            # this only happens when formatting a toc from env.tocs -- in this
-            # case we don't want to include the subtree
-            raise nodes.SkipNode
-
-        def visit_index(self, node):
-            raise nodes.SkipNode
-
-        def visit_glossary(self, node):
-            pass
-        def depart_glossary(self, node):
-            pass
-
-        # these are only handled specially in the SmartyPantsHTMLTranslator
-        def visit_literal_emphasis(self, node):
-            return self.visit_emphasis(node)
-        def depart_literal_emphasis(self, node):
-            return self.depart_emphasis(node)
-
-        def depart_title(self, node):
-            close_tag = self.context[-1]
-            if buildername != 'htmlhelp' and \
-                   close_tag.startswith(('</h', '</a></h')) and \
-                   node.parent.hasattr('ids') and node.parent['ids']:
-                aname = node.parent['ids'][0]
-                # add permalink anchor
-                self.body.append(u'<a class="headerlink" href="#%s" ' % aname +
-                                 u'title="Permalink to this headline">\u00B6</a>')
-            BaseTranslator.depart_title(self, node)
-
-
-    class SmartyPantsHTMLTranslator(HTMLTranslator):
-        """
-        Handle ordinary text via smartypants, converting quotes and dashes
-        to the correct entities.
-        """
-
-        def __init__(self, *args, **kwds):
-            self.no_smarty = 0
-            HTMLTranslator.__init__(self, *args, **kwds)
-
-        def visit_literal(self, node):
-            self.no_smarty += 1
-            try:
-                # this raises SkipNode
-                HTMLTranslator.visit_literal(self, node)
-            finally:
-                self.no_smarty -= 1
-
-        def visit_literal_emphasis(self, node):
-            self.no_smarty += 1
-            self.visit_emphasis(node)
-
-        def depart_literal_emphasis(self, node):
-            self.depart_emphasis(node)
-            self.no_smarty -= 1
-
-        def visit_desc_signature(self, node):
-            self.no_smarty += 1
-            HTMLTranslator.visit_desc_signature(self, node)
-
-        def depart_desc_signature(self, node):
-            self.no_smarty -= 1
-            HTMLTranslator.depart_desc_signature(self, node)
-
-        def visit_productionlist(self, node):
-            self.no_smarty += 1
-            try:
-                HTMLTranslator.visit_productionlist(self, node)
-            finally:
-                self.no_smarty -= 1
-
-        def encode(self, text):
-            text = HTMLTranslator.encode(self, text)
-            if self.no_smarty <= 0:
-                text = sphinx_smarty_pants(text)
-            return text
-
-    if config.get('use_smartypants', False):
-        return SmartyPantsHTMLTranslator
-    else:
-        return HTMLTranslator


More information about the Python-checkins mailing list