Commits

Jon Waltman  committed 7ba6a64

Add Docutils-native XML and pseudo-XML builders

  • Participants
  • Parent commits 7375761

Comments (0)

Files changed (10)

 * PR#88: Added the "Sphinx Developer's Guide" (:file:`doc/devguide.rst`)
   which outlines the basic development process of the Sphinx project.
 
+* Added the Docutils-native XML and pseudo-XML builders.  See
+  :class:`XMLBuilder` and :class:`PseudoXMLBuilder`.
+
 
 Release 1.1.3 (Mar 10, 2012)
 ============================

File doc/Makefile

 I18NSPHINXOPTS   = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) $(O) .
 
 .PHONY: help clean html dirhtml singlehtml text man pickle json htmlhelp \
-	qthelp devhelp epub latex latexpdf changes linkcheck doctest
+	qthelp devhelp epub latex latexpdf changes linkcheck doctest xml \
+	pseudoxml
 
 help:
 	@echo "Please use \`make <target>' where <target> is one of"
 	@echo "Running Texinfo files through makeinfo..."
 	make -C _build/texinfo info
 	@echo "makeinfo finished; the Info files are in _build/texinfo."
+
+xml:
+	$(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) _build/xml
+	@echo
+	@echo "Build finished. The XML files are in _build/XML."
+
+pseudoxml:
+	$(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) _build/pseudoxml
+	@echo
+	@echo "Build finished. The pseudo-XML files are in _build/pseudoxml."

File doc/builders.rst

 
    Its name is ``linkcheck``.
 
+.. module:: sphinx.builders.xml
+.. class:: XMLBuilder
+
+   This builder produces Docutils-native XML files.  The output can be
+   transformed with standard XML tools such as XSLT processors into arbitrary
+   final forms.
+
+   Its name is ``xml``.
+
+   .. versionadded:: 1.2
+
+.. class:: PseudoXMLBuilder
+
+   This builder is used for debugging the Sphinx/Docutils "Reader to Transform
+   to Writer" pipeline. It produces compact pretty-printed "pseudo-XML", files
+   where nesting is indicated by indentation (no end-tags). External
+   attributes for all elements are output, and internal attributes for any
+   leftover "pending" elements are also given.
+
+   Its name is ``pseudoxml``.
+
+   .. versionadded:: 1.2
+
 
 Built-in Sphinx extensions that offer more builders are:
 

File doc/config.rst

    .. versionadded:: 1.2
 
 
+Options for the XML builder
+---------------------------
+
+.. confval:: xml_pretty
+
+   If True, pretty-print the XML.  Default is ``True``.
+
+   .. versionadded:: 1.2
+
+
 .. rubric:: Footnotes
 
 .. [1] A note on available globbing syntax: you can use the standard shell

File sphinx/builders/__init__.py

     'linkcheck':  ('linkcheck', 'CheckExternalLinksBuilder'),
     'websupport': ('websupport', 'WebSupportBuilder'),
     'gettext':    ('gettext', 'MessageCatalogBuilder'),
+    'xml':        ('xml', 'XMLBuilder'),
+    'pseudoxml':  ('xml', 'PseudoXMLBuilder'),
 }

File sphinx/builders/xml.py

+# -*- coding: utf-8 -*-
+"""
+    sphinx.builders.xml
+    ~~~~~~~~~~~~~~~~~~~
+
+    Docutils-native XML and pseudo-XML builders.
+
+    :copyright: Copyright 2007-2012 by the Sphinx team, see AUTHORS.
+    :license: BSD, see LICENSE for details.
+"""
+
+import codecs
+from os import path
+
+from docutils.io import StringOutput
+
+from sphinx.builders import Builder
+from sphinx.util.osutil import ensuredir, os_path
+from sphinx.writers.xml import XMLWriter, PseudoXMLWriter
+
+class XMLBuilder(Builder):
+    """
+    Builds Docutils-native XML.
+    """
+    name = 'xml'
+    format = 'xml'
+    out_suffix = '.xml'
+
+    _writer_class = XMLWriter
+
+    def init(self):
+        pass
+
+    def get_outdated_docs(self):
+        for docname in self.env.found_docs:
+            if docname not in self.env.all_docs:
+                yield docname
+                continue
+            targetname = self.env.doc2path(docname, self.outdir,
+                                           self.out_suffix)
+            try:
+                targetmtime = path.getmtime(targetname)
+            except Exception:
+                targetmtime = 0
+            try:
+                srcmtime = path.getmtime(self.env.doc2path(docname))
+                if srcmtime > targetmtime:
+                    yield docname
+            except EnvironmentError:
+                # source doesn't exist anymore
+                pass
+
+    def get_target_uri(self, docname, typ=None):
+        return ''
+
+    def prepare_writing(self, docnames):
+        self.writer = self._writer_class(self)
+
+    def write_doc(self, docname, doctree):
+        destination = StringOutput(encoding='utf-8')
+        self.writer.write(doctree, destination)
+        outfilename = path.join(self.outdir, os_path(docname) + self.out_suffix)
+        ensuredir(path.dirname(outfilename))
+        try:
+            f = codecs.open(outfilename, 'w', 'utf-8')
+            try:
+                f.write(self.writer.output)
+            finally:
+                f.close()
+        except (IOError, OSError), err:
+            self.warn("error writing file %s: %s" % (outfilename, err))
+
+    def finish(self):
+        pass
+
+
+class PseudoXMLBuilder(XMLBuilder):
+    """
+    Builds pseudo-XML for display purposes.
+    """
+    name = 'pseudoxml'
+    format = 'pseudoxml'
+    out_suffix = '.pseudoxml'
+
+    _writer_class = PseudoXMLWriter

File sphinx/config.py

 
         # gettext options
         gettext_compact = (True, 'gettext'),
+
+        # XML options
+        xml_pretty = (True, 'env'),
     )
 
     def __init__(self, dirname, filename, overrides, tags):

File sphinx/quickstart.py

 may add the Sphinx directory to PATH.
 
 If you don't have Sphinx installed, grab it from
-http://sphinx-doc.org/ 
+http://sphinx-doc.org/
 endef
 $(error $(MSG))
 endif
 \t@echo "  info       to make Texinfo files and run them through makeinfo"
 \t@echo "  gettext    to make PO message catalogs"
 \t@echo "  changes    to make an overview of all changed/added/deprecated items"
+\t@echo "  xml        to make Docutils-native XML files"
+\t@echo "  pseudoxml  to make pseudoxml-XML files for display purposes"
 \t@echo "  linkcheck  to check all external links for integrity"
 \t@echo "  doctest    to run all doctests embedded in the documentation \
 (if enabled)"
 \t$(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
 \t@echo "Testing of doctests in the sources finished, look at the " \\
 \t      "results in $(BUILDDIR)/doctest/output.txt."
+
+xml:
+\t$(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml
+\t@echo
+\t@echo "Build finished. The XML files are in $(BUILDDIR)/xml."
+
+pseudoxml:
+\t$(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml
+\t@echo
+\t@echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml."
 '''
 
 BATCHFILE = '''\
 \techo.  texinfo    to make Texinfo files
 \techo.  gettext    to make PO message catalogs
 \techo.  changes    to make an overview over all changed/added/deprecated items
+\techo.  xml        to make Docutils-native XML files
+\techo.  pseudoxml  to make pseudoxml-XML files for display purposes
 \techo.  linkcheck  to check all external links for integrity
 \techo.  doctest    to run all doctests embedded in the documentation if enabled
 \tgoto end
 \tgoto end
 )
 
+if "%%1" == "xml" (
+\t%%SPHINXBUILD%% -b xml %%ALLSPHINXOPTS%% %%BUILDDIR%%/xml
+\tif errorlevel 1 exit /b 1
+\techo.
+\techo.Build finished. The XML files are in %%BUILDDIR%%/xml.
+\tgoto end
+)
+
+if "%%1" == "pseudoxml" (
+\t%%SPHINXBUILD%% -b pseudoxml %%ALLSPHINXOPTS%% %%BUILDDIR%%/pseudoxml
+\tif errorlevel 1 exit /b 1
+\techo.
+\techo.Build finished. The pseudo-XML files are in %%BUILDDIR%%/pseudoxml.
+\tgoto end
+)
+
 :end
 '''
 

File sphinx/writers/xml.py

+# -*- coding: utf-8 -*-
+"""
+    sphinx.writers.xml
+    ~~~~~~~~~~~~~~~~~~
+
+    Docutils-native XML and pseudo-XML writers.
+
+    :copyright: Copyright 2007-2012 by the Sphinx team, see AUTHORS.
+    :license: BSD, see LICENSE for details.
+"""
+
+from docutils import writers
+from docutils.writers.docutils_xml import Writer as BaseXMLWriter
+from docutils.writers.docutils_xml import XMLTranslator as BaseXMLTranslator
+
+
+class XMLWriter(BaseXMLWriter):
+
+    def __init__(self, builder):
+        BaseXMLWriter.__init__(self)
+        self.builder = builder
+
+    def translate(self, *args, **kwargs):
+        self.document.settings.newlines = \
+          self.document.settings.indents = \
+          self.builder.env.config.xml_pretty
+        self.document.settings.xml_declaration = True
+        self.document.settings.doctype_declaration = True
+        return BaseXMLWriter.translate(self)
+
+
+class XMLTranslator(BaseXMLTranslator):
+    def __init__(self, builder, doc):
+        BaseXMLTranslator.__init__(self, doc)
+        self.builder = builder
+
+
+class PseudoXMLWriter(writers.Writer):
+
+    supported = ('pprint', 'pformat', 'pseudoxml')
+    """Formats this writer supports."""
+
+    config_section = 'pseudoxml writer'
+    config_section_dependencies = ('writers',)
+
+    output = None
+    """Final translated form of `document`."""
+
+    def __init__(self, builder):
+        writers.Writer.__init__(self)
+        self.builder = builder
+
+    def translate(self):
+        self.output = self.document.pformat()
+
+    def supports(self, format):
+        """This writer supports all format-specific elements."""
+        return True

File tests/test_build.py

 @with_app(buildername='singlehtml', cleanenv=True)
 def test_singlehtml(app):
     app.builder.build_all()
+
+@with_app(buildername='xml')
+def test_xml(app):
+    app.builder.build_all()
+
+@with_app(buildername='pseudoxml')
+def test_pseudoxml(app):
+    app.builder.build_all()