Change the source parsers feature a bit:

* parsers -> source_parsers
* add docs
* require fully qualified name or class
* add test for it
This commit is contained in:
Georg Brandl 2015-03-08 13:11:28 +01:00
parent 0cb9b1db02
commit ad612fb03d
12 changed files with 65 additions and 36 deletions

View File

@ -18,6 +18,8 @@ Features added
declaraction. #1577, #1744
* The :confval:`source_suffix` config value can now be a list of multiple
suffixes.
* Add the ability to specify source parsers by source suffix with the
:confval:`source_parsers` config value.
Bugs fixed
----------

View File

@ -92,6 +92,19 @@ General configuration
.. versionadded:: 0.5
Previously, Sphinx accepted only UTF-8 encoded sources.
.. confval:: source_parsers
If given, a dictionary of parser classes for different source suffices. The
keys are the suffix, the values can be either a class or a string giving a
fully-qualified name of a parser class. Files with a suffix that is not in
the dictionary will be parsed with the default reStructuredText parser.
For example::
source_parsers = {'.md': 'some.markdown.module.Parser'}
.. versionadded:: 1.3
.. confval:: master_doc
The document name of the "master" document, that is, the document that

View File

@ -37,6 +37,7 @@ from sphinx.domains.std import GenericObject, Target, StandardDomain
from sphinx.builders import BUILTIN_BUILDERS
from sphinx.environment import BuildEnvironment, SphinxStandaloneReader
from sphinx.util import pycompat # imported for side-effects
from sphinx.util import import_object
from sphinx.util.tags import Tags
from sphinx.util.osutil import ENOENT
from sphinx.util.console import bold, lightgray, darkgray, darkgreen, \
@ -457,22 +458,7 @@ class Sphinx(object):
def import_object(self, objname, source=None):
"""Import an object from a 'module.name' string."""
try:
module, name = objname.rsplit('.', 1)
except ValueError as err:
raise ExtensionError('Invalid full object name %s' % objname +
(source and ' (needed for %s)' % source or ''),
err)
try:
return getattr(__import__(module, None, None, [name]), name)
except ImportError as err:
raise ExtensionError('Could not import %s' % module +
(source and ' (needed for %s)' % source or ''),
err)
except AttributeError as err:
raise ExtensionError('Could not find %s' % objname +
(source and ' (needed for %s)' % source or ''),
err)
return import_object(objname, source=None)
# event interface

View File

@ -53,9 +53,9 @@ class Config(object):
master_doc = ('contents', 'env'),
source_suffix = (['.rst'], 'env'),
source_encoding = ('utf-8-sig', 'env'),
source_parsers = ({}, 'env'),
exclude_patterns = ([], 'env'),
default_role = (None, 'env'),
parsers = ({}, 'env'),
add_function_parentheses = (True, 'env'),
add_module_names = (True, 'env'),
trim_footnote_reference_space = (False, 'env'),

View File

@ -38,7 +38,7 @@ from docutils.frontend import OptionParser
from sphinx import addnodes
from sphinx.util import url_re, get_matching_docs, docname_join, split_into, \
FilenameUniqDict, get_figtype
FilenameUniqDict, get_figtype, import_object
from sphinx.util.nodes import clean_astext, make_refnode, WarningStream, is_translatable
from sphinx.util.osutil import SEP, find_catalog_files, getcwd, fs_encoding
from sphinx.util.console import bold, purple
@ -102,23 +102,13 @@ class SphinxStandaloneReader(standalone.Reader):
DefaultSubstitutions, MoveModuleTargets, HandleCodeBlocks,
AutoNumbering, SortIds, RemoveTranslatableInline]
@staticmethod
def get_parser_class(parser_name):
"""Return the Parser class from the `parser_name` module."""
# You passed a class instead
if not isinstance(parser_name, string_types):
return parser_name
try:
module = __import__(parser_name, globals(), locals(), ['Parser'], level=1)
except ImportError:
module = __import__(parser_name, globals(), locals(), ['Parser'], level=0)
return module.Parser
def __init__(self, parsers={}, *args, **kwargs):
standalone.Reader.__init__(self, *args, **kwargs)
self.parser_map = {}
for suffix, parser_name in parsers.items():
self.parser_map[suffix] = self.get_parser_class(parser_name)()
for suffix, parser_class in parsers.items():
if isinstance(parser_class, string_types):
parser_class = import_object(parser_class, 'source parser')
self.parser_map[suffix] = parser_class()
def read(self, source, parser, settings):
self.source = source
@ -785,7 +775,7 @@ class BuildEnvironment:
codecs.register_error('sphinx', self.warn_and_replace)
# publish manually
reader = SphinxStandaloneReader(parsers=self.config.parsers)
reader = SphinxStandaloneReader(parsers=self.config.source_parsers)
pub = Publisher(reader=reader,
writer=SphinxDummyWriter(),
destination_class=NullOutput)

View File

@ -60,7 +60,7 @@ import inspect
import posixpath
from types import ModuleType
from six import text_type, string_types
from six import text_type
from docutils.parsers.rst import directives
from docutils.statemachine import ViewList
from docutils import nodes

View File

@ -29,7 +29,7 @@ from docutils.utils import relative_path
import jinja2
import sphinx
from sphinx.errors import PycodeError, SphinxParallelError
from sphinx.errors import PycodeError, SphinxParallelError, ExtensionError
from sphinx.util.console import strip_colors
from sphinx.util.osutil import fs_encoding
@ -494,3 +494,22 @@ def get_figtype(node):
return 'code-block'
return None
def import_object(objname, source=None):
try:
module, name = objname.rsplit('.', 1)
except ValueError as err:
raise ExtensionError('Invalid full object name %s' % objname +
(source and ' (needed for %s)' % source or ''),
err)
try:
return getattr(__import__(module, None, None, [name]), name)
except ImportError as err:
raise ExtensionError('Could not import %s' % module +
(source and ' (needed for %s)' % source or ''),
err)
except AttributeError as err:
raise ExtensionError('Could not find %s' % objname +
(source and ' (needed for %s)' % source or ''),
err)

View File

@ -12,7 +12,8 @@ jsmath_path = 'dummy.js'
templates_path = ['_templates']
master_doc = 'contents'
source_suffix = ['.txt', '.add']
source_suffix = ['.txt', '.add', '.foo']
source_parsers = {'.foo': 'parsermod.Parser'}
project = 'Sphinx <Tests>'
copyright = '2010-2015, Georg Brandl & Team'

View File

@ -26,6 +26,7 @@ Contents:
extensions
footnote
lists
otherext
http://sphinx-doc.org/
Latest reference <http://sphinx-doc.org/latest/>

2
tests/root/otherext.foo Normal file
View File

@ -0,0 +1,2 @@
The contents of this file are ignored.
The file is "parsed" using Parser in the tests/root/parsermod.py file.

12
tests/root/parsermod.py Normal file
View File

@ -0,0 +1,12 @@
from docutils.parsers import Parser
from docutils import nodes
class Parser(Parser):
def parse(self, input, document):
section = nodes.section(ids=['id1'])
section += nodes.title('Generated section', 'Generated section')
document += section
def get_transforms(self):
return []

View File

@ -289,6 +289,9 @@ HTML_XPATH = {
(".//a[@class='fn-backref'][@href='#id3']", r"\[3\]"),
(".//a[@class='fn-backref'][@href='#id4']", r"\[bar\]"),
],
'otherext.html': [
(".//h1", "Generated section"),
]
}