Example #1
0
def mock_sphinx_env(conf=None, srcdir=None, document=None):
    """Set up an environment, to parse sphinx roles/directives,
    outside of a `sphinx-build`.

    :param conf: a dictionary representation of the sphinx `conf.py`
    :param srcdir: a path to a source directory
        (for example, can be used for `include` statements)

    This primarily copies the code in `sphinx.util.docutils.docutils_namespace`
    and `sphinx.util.docutils.sphinx_domains`.
    """
    # store currently loaded roles/directives, so we can revert on exit
    _directives = copy.copy(directives._directives)
    _roles = copy.copy(roles._roles)
    # Monkey-patch directive and role dispatch,
    # so that sphinx domain-specific markup takes precedence.
    app = minimal_sphinx_app(configuration=conf, sourcedir=srcdir)
    _sphinx_domains = sphinx_domains(app.env)
    _sphinx_domains.enable()
    if document is not None:
        document.settings.env = app.env
    try:
        yield app
    finally:
        # revert loaded roles/directives
        directives._directives = _directives
        roles._roles = _roles
        # TODO unregister nodes (see `sphinx.util.docutils.docutils_namespace`)
        for node in list(additional_nodes):
            unregister_node(node)
            additional_nodes.discard(node)
        # revert directive/role function (see `sphinx.util.docutils.sphinx_domains`)
        _sphinx_domains.disable()
Example #2
0
    def read_doc(self, docname: str) -> None:
        """Parse a file and add/update inventory entries for the doctree."""
        self.env.prepare_settings(docname)

        # Add confdir/docutils.conf to dependencies list if exists
        docutilsconf = path.join(self.confdir, 'docutils.conf')
        if path.isfile(docutilsconf):
            self.env.note_dependency(docutilsconf)

        with sphinx_domains(self.env), rst.default_role(
                docname, self.config.default_role):
            doctree = read_doc(self.app, self.env, self.env.doc2path(docname))

        # store time of reading, for outdated files detection
        # (Some filesystems have coarse timestamp resolution;
        # therefore time.time() can be older than filesystem's timestamp.
        # For example, FAT32 has 2sec timestamp resolution.)
        self.env.all_docs[docname] = max(
            time.time(), path.getmtime(self.env.doc2path(docname)))

        # cleanup
        self.env.temp_data.clear()
        self.env.ref_context.clear()

        self.write_doctree(docname, doctree)
Example #3
0
    def read_doc(self, docname, app=None):
        # type: (unicode, Sphinx) -> None
        """Parse a file and add/update inventory entries for the doctree."""
        self.prepare_settings(docname)

        docutilsconf = path.join(self.srcdir, 'docutils.conf')
        # read docutils.conf from source dir, not from current dir
        OptionParser.standard_config_files[1] = docutilsconf
        if path.isfile(docutilsconf):
            self.note_dependency(docutilsconf)

        with sphinx_domains(self), rst.default_role(docname,
                                                    self.config.default_role):
            doctree = read_doc(self.app, self, self.doc2path(docname))

        # store time of reading, for outdated files detection
        # (Some filesystems have coarse timestamp resolution;
        # therefore time.time() can be older than filesystem's timestamp.
        # For example, FAT32 has 2sec timestamp resolution.)
        self.all_docs[docname] = max(time.time(),
                                     path.getmtime(self.doc2path(docname)))

        # cleanup
        self.temp_data.clear()
        self.ref_context.clear()

        self.write_doctree(docname, doctree)
Example #4
0
def sphinx_env(app_env: SphinxAppEnv):
    """This context enters the standard sphinx contexts,
    then registers the roles, directives and nodes saved in the app_env.

    The standard sphinx contexts:

    - Patch docutils.languages.get_language(), to suppress reporter warnings
    - Temporarily sets `os.environ['DOCUTILSCONFIG']` to the sphinx confdir
    - Saves copies of roles._roles and directives._directives & resets them on exit
    - Un-registers additional nodes (set via `register_node`) on exit
      (by deleting `GenericNodeVisitor` visit/depart methods)
    - Patches roles.roles and directives.directives functions to also look in domains
    """
    with patch_docutils(
            app_env.app.confdir), docutils_namespace(), sphinx_domains(
                app_env.app.env):
        from docutils.parsers.rst import directives, roles
        from sphinx.util.docutils import register_node

        if app_env.roles:
            roles._roles.update(app_env.roles)
        if app_env.directives:
            directives._directives.update(app_env.directives)
        for node in app_env.additional_nodes:
            register_node(node)
        # TODO how to make `unregister_node` thread safe

        yield
Example #5
0
 def make_html(self, docstring):
     with Timer('make_html'):
         with open(self.doc_file, 'w') as f:
             f.write(docstring)
         app = self.app
         docname = 'index'
         with sphinx_domains(app.builder.env), rst.default_role(
                 docname, app.builder.env.config.default_role):
             doctree = read_doc(app.builder.env.app, app.builder.env,
                                app.builder.env.doc2path(docname))
         for domain in app.builder.env.domains.values():
             domain.process_doc(app.builder.env, docname, doctree)
         app.emit('doctree-read', doctree)
         app.builder.prepare_writing({docname})
         doctree = app.builder.env.get_and_resolve_doctree(
             docname, app.builder, doctree)
         app.builder.write_doc_serialized(docname, doctree)
         doctree.settings = app.builder.docsettings
         app.builder.secnumbers = app.builder.env.toc_secnumbers.get(
             docname, {})
         app.builder.fignumbers = app.builder.env.toc_fignumbers.get(
             docname, {})
         app.builder.imgpath = relative_uri(
             app.builder.get_target_uri(docname), '_images')
         app.builder.dlpath = relative_uri(
             app.builder.get_target_uri(docname), '_downloads')
         app.builder.current_docname = docname
         app.builder.docwriter.write(doctree, self.destination)
         app.builder.docwriter.assemble_parts()
         body = app.builder.docwriter.parts['fragment']
     return body
Example #6
0
    def read_doc(self, docname):
        # type: (unicode) -> None
        """Parse a file and add/update inventory entries for the doctree."""
        self.env.prepare_settings(docname)

        # Add confdir/docutils.conf to dependencies list if exists
        docutilsconf = path.join(self.confdir, 'docutils.conf')
        if path.isfile(docutilsconf):
            self.env.note_dependency(docutilsconf)

        with sphinx_domains(self.env), rst.default_role(docname, self.config.default_role):
            doctree = read_doc(self.app, self.env, self.env.doc2path(docname))

        # store time of reading, for outdated files detection
        # (Some filesystems have coarse timestamp resolution;
        # therefore time.time() can be older than filesystem's timestamp.
        # For example, FAT32 has 2sec timestamp resolution.)
        self.env.all_docs[docname] = max(time.time(),
                                         path.getmtime(self.env.doc2path(docname)))

        # cleanup
        self.env.temp_data.clear()
        self.env.ref_context.clear()

        self.write_doctree(docname, doctree)
Example #7
0
    def read_doc(self, docname: str) -> None:
        """Parse a file and add/update inventory entries for the doctree."""
        self.env.prepare_settings(docname)

        # Add confdir/docutils.conf to dependencies list if exists
        docutilsconf = path.join(self.confdir, 'docutils.conf')
        if path.isfile(docutilsconf):
            self.env.note_dependency(docutilsconf)

        filename = self.env.doc2path(docname)
        filetype = get_filetype(self.app.config.source_suffix, filename)
        publisher = self.app.registry.get_publisher(self.app, filetype)
        with sphinx_domains(self.env), rst.default_role(docname, self.config.default_role):
            # set up error_handler for the target document
            codecs.register_error('sphinx', UnicodeDecodeErrorHandler(docname))  # type: ignore

            publisher.set_source(source_path=filename)
            publisher.publish()
            doctree = publisher.document

        # store time of reading, for outdated files detection
        # (Some filesystems have coarse timestamp resolution;
        # therefore time.time() can be older than filesystem's timestamp.
        # For example, FAT32 has 2sec timestamp resolution.)
        self.env.all_docs[docname] = max(time.time(),
                                         path.getmtime(self.env.doc2path(docname)))

        # cleanup
        self.env.temp_data.clear()
        self.env.ref_context.clear()

        self.write_doctree(docname, doctree)
Example #8
0
 def _parse(app: Sphinx, code: str) -> document:
     with NamedTemporaryFile("w+", suffix=".rst", dir=app.env.srcdir) as f:
         f.write(code)
         f.flush()
         app.env.prepare_settings(f.name)
         with sphinx_domains(app.env), rst.default_role(
                 f.name, app.config.default_role):
             return read_doc(app, app.env, f.name)
Example #9
0
def find_autoasdf_directives(env, filename):

    docname = env.path2doc(filename)
    env.prepare_settings(docname)
    with sphinx_domains(env), rst.default_role(docname,
                                               env.config.default_role):
        doctree = read_doc(env.app, env, env.doc2path(docname))

    return doctree.traverse(schema_def)
Example #10
0
def mock_sphinx_env_compat(
    conf=None,
    srcdir=None,
    document=None,
    with_builder=False,
    raise_on_warning=False,
):
    """Set up an environment, to parse sphinx roles/directives,
    outside of a `sphinx-build`.

    :param conf: a dictionary representation of the sphinx `conf.py`
    :param srcdir: a path to a source directory
        (for example, can be used for `include` statements)

    This primarily copies the code in `sphinx.util.docutils.docutils_namespace`
    and `sphinx.util.docutils.sphinx_domains`.
    """
    with tempfile.TemporaryDirectory() as tempdir:
        # store currently loaded roles/directives, so we can revert on exit
        _directives = copy.copy(directives._directives)
        _roles = copy.copy(roles._roles)

        # creating a builder attempts to make the doctreedir
        app = Sphinx(
            srcdir=srcdir,
            confdir=None,
            outdir=tempdir,
            doctreedir=tempdir,
            confoverrides=conf,
            buildername=with_builder,
            warningiserror=raise_on_warning,
            keep_going=True,
        )
        _sphinx_domains = sphinx_domains(app.env)
        _sphinx_domains.enable()

        if document is not None:
            document.settings.env = app.env

        try:
            yield app
        finally:
            # NOTE: the following cleanup is to avoid warnings while creating a Sphinx
            # Application multiple times

            # revert loaded roles/directives
            directives._directives = _directives
            roles._roles = _roles

            for node in list(additional_nodes):
                unregister_node(node)
                additional_nodes.discard(node)

            # revert directive/role function (ee
            # `sphinx.util.docutils.sphinx_domains`)
            _sphinx_domains.disable()
Example #11
0
def settings(app):
    texescape.init()  # otherwise done by the latex builder
    optparser = frontend.OptionParser(components=(RstParser, HTMLWriter,
                                                  LaTeXWriter))
    settings = optparser.get_default_values()
    settings.env = app.builder.env
    settings.env.temp_data['docname'] = 'dummy'
    domain_context = sphinx_domains(settings.env)
    domain_context.enable()
    yield settings
    domain_context.disable()
Example #12
0
def settings(app):
    texescape.init()  # otherwise done by the latex builder
    optparser = frontend.OptionParser(
        components=(RstParser, HTMLWriter, LaTeXWriter))
    settings = optparser.get_default_values()
    settings.env = app.builder.env
    settings.env.temp_data['docname'] = 'dummy'
    domain_context = sphinx_domains(settings.env)
    domain_context.enable()
    yield settings
    domain_context.disable()
Example #13
0
def setup_module():
    global app, settings, parser, domain_context
    texescape.init()  # otherwise done by the latex builder
    app = TestApp()
    optparser = frontend.OptionParser(
        components=(rst.Parser, HTMLWriter, LaTeXWriter))
    settings = optparser.get_default_values()
    settings.env = app.builder.env
    settings.env.temp_data['docname'] = 'dummy'
    parser = rst.Parser()
    domain_context = sphinx_domains(settings.env)
    domain_context.enable()
Example #14
0
def _get_doctree(docstring: str):
    docname = "_docstring"
    builder = _get_builder()
    env = builder.env

    env.prepare_settings(docname)
    with sphinx_domains(env), rst.default_role(docname,
                                               builder.config.default_role):
        document = publish_doctree(docstring, settings_overrides=env.settings)
        env.apply_post_transforms(document, docname)

    return document
Example #15
0
def setup_module():
    global app, settings, parser, domain_context
    texescape.init()  # otherwise done by the latex builder
    app = TestApp()
    optparser = frontend.OptionParser(
        components=(rst.Parser, HTMLWriter, LaTeXWriter))
    settings = optparser.get_default_values()
    settings.env = app.builder.env
    settings.env.temp_data['docname'] = 'dummy'
    parser = rst.Parser()
    domain_context = sphinx_domains(settings.env)
    domain_context.enable()
Example #16
0
def sphinx_state(local_app):
    """
    Fixture which will provide a sphinx state for use in testing sphinx
    directives.

    Yields:
        :class:`docutils.parsers.rst.states.State`: A state for use in testing
            directive functionality.
    """
    # Get the environment and decorate it with what sphinx may need for the
    # parsing.
    env = local_app.env
    env.temp_data["docname"] = "test"  # A fake document name

    # Create a document and inliner object, to be perfectly honest not sure
    # exactly what these are or do, but needed to get the directive to run.
    document = new_document(__file__)
    document.settings.pep_references = 1
    document.settings.rfc_references = 1
    document.settings.env = env
    document.settings.tab_width = 4
    inliner = Inliner()
    inliner.init_customizations(document.settings)

    # Create a state machine so that we can get a state to pass back.
    statemachine = RSTStateMachine(state_classes=state_classes, initial_state="Body")
    statemachine.input_lines = StringList([""] * 40)
    state = statemachine.get_state()
    state.document = document
    state.memo = Struct(
        inliner=inliner,
        language=en,
        title_styles=[],
        reporter=document.reporter,
        document=document,
        section_level=0,
        section_bubble_up_kludge=False,
    )

    state.memo.reporter.get_source_and_line = statemachine.get_source_and_line

    # The environemnt isn't normally available on the state in sphinx, but it's
    # done here to make testing easier.
    state.env = env

    # Sphinx monkeypatches docutils when run. This is how it get's
    # monkeypatched so that the python directives and roles can be found
    with sphinx_domains(env):

        # Provide the state back to the test.
        yield state
Example #17
0
def settings(app):
    texescape.init()  # otherwise done by the latex builder
    optparser = frontend.OptionParser(components=(RstParser, HTMLWriter,
                                                  LaTeXWriter))
    settings = optparser.get_default_values()
    settings.smart_quotes = True
    settings.env = app.builder.env
    settings.env.temp_data['docname'] = 'dummy'
    settings.contentsname = 'dummy'
    settings.rfc_base_url = 'http://tools.ietf.org/html/'
    domain_context = sphinx_domains(settings.env)
    domain_context.enable()
    yield settings
    domain_context.disable()
Example #18
0
def parse(app, text, docname='index'):
    # type: (Sphinx, str, str) -> nodes.document
    """Parse a string as reStructuredText with Sphinx application."""
    try:
        app.env.temp_data['docname'] = docname
        parser = RSTParser()
        parser.set_application(app)
        with sphinx_domains(app.env):
            return publish_doctree(text, path.join(app.srcdir, docname + '.rst'),
                                   reader=SphinxStandaloneReader(app),
                                   parser=parser,
                                   settings_overrides={'env': app.env,
                                                       'gettext_compact': True})
    finally:
        app.env.temp_data.pop('docname', None)
Example #19
0
def parse(app: Sphinx, text: str, docname: str = "index") -> nodes.document:
    """Parse a string as MystMarkdown with Sphinx application."""
    app.env.temp_data["docname"] = docname
    app.env.all_docs[docname] = time.time()
    reader = SphinxStandaloneReader()
    reader.setup(app)
    parser = MystParser()
    parser.set_application(app)
    with sphinx_domains(app.env):
        return publish_doctree(
            text,
            path.join(app.srcdir, docname + ".md"),
            reader=reader,
            parser=parser,
            parser_name="markdown",
            settings_overrides={"env": app.env, "gettext_compact": True},
        )
Example #20
0
def settings(app):
    texescape.init()  # otherwise done by the latex builder
    with warnings.catch_warnings():
        warnings.filterwarnings('ignore', category=DeprecationWarning)
        # DeprecationWarning: The frontend.OptionParser class will be replaced
        # by a subclass of argparse.ArgumentParser in Docutils 0.21 or later.
        optparser = frontend.OptionParser(components=(RstParser, HTMLWriter,
                                                      LaTeXWriter),
                                          defaults=default_settings)
    settings = optparser.get_default_values()
    settings.smart_quotes = True
    settings.env = app.builder.env
    settings.env.temp_data['docname'] = 'dummy'
    settings.contentsname = 'dummy'
    domain_context = sphinx_domains(settings.env)
    domain_context.enable()
    yield settings
    domain_context.disable()
Example #21
0
    def read_doc(self, docname, app=None):
        # type: (unicode, Sphinx) -> None
        """Parse a file and add/update inventory entries for the doctree."""
        self.prepare_settings(docname)

        docutilsconf = path.join(self.srcdir, 'docutils.conf')
        # read docutils.conf from source dir, not from current dir
        OptionParser.standard_config_files[1] = docutilsconf
        if path.isfile(docutilsconf):
            self.note_dependency(docutilsconf)

        with sphinx_domains(self), rst.default_role(docname, self.config.default_role):
            doctree = read_doc(self.app, self, self.doc2path(docname))

        # post-processing
        for domain in itervalues(self.domains):
            domain.process_doc(self, docname, doctree)

        # allow extension-specific post-processing
        if app:
            app.emit('doctree-read', doctree)

        # store time of reading, for outdated files detection
        # (Some filesystems have coarse timestamp resolution;
        # therefore time.time() can be older than filesystem's timestamp.
        # For example, FAT32 has 2sec timestamp resolution.)
        self.all_docs[docname] = max(
            time.time(), path.getmtime(self.doc2path(docname)))

        if self.versioning_condition:
            # add uids for versioning
            versioning.prepare(doctree)

        # cleanup
        self.temp_data.clear()
        self.ref_context.clear()

        self.write_doctree(docname, doctree)
Example #22
0
    def __enter__(self):
        """If `load_sphinx_env=True`, we set up an environment,
        to parse sphinx roles/directives, outside of a `sphinx-build`.

        This primarily copies the code in `sphinx.util.docutils.docutils_namespace`
        and `sphinx.util.docutils.sphinx_domains`.
        """
        if not self.load_sphinx_env:
            return super().__enter__()

        # store currently loaded roles/directives, so we can revert on exit
        self._directives = copy.copy(directives._directives)
        self._roles = copy.copy(roles._roles)
        # Monkey-patch directive and role dispatch,
        # so that sphinx domain-specific markup takes precedence.
        self._env = self.mock_sphinx_env(
            configuration=self.sphinx_conf, sourcedir=self.sphinx_srcdir
        ).env
        from sphinx.util.docutils import sphinx_domains

        self._sphinx_domains = sphinx_domains(self._env)
        self._sphinx_domains.enable()

        return super().__enter__()
Example #23
0
start_time = time()
# app.builder.env._read_serial(docnames, app.builder.env.app)
for docname in docnames:
    print('docname', docname)
    app.emit('env-purge-doc', app.builder.env, docname)
    app.builder.env.clear_doc(docname)
    # app.builder.env.read_doc(docname, app)
    app.builder.env.prepare_settings(docname)
    docutilsconf = path.join(app.builder.env.srcdir, 'docutils.conf')
    # read docutils.conf from source dir, not from current dir
    OptionParser.standard_config_files[1] = docutilsconf
    if path.isfile(docutilsconf):
        app.builder.env.note_dependency(docutilsconf)

    with sphinx_domains(app.builder.env), rst.default_role(docname, app.builder.env.config.default_role):
        doctree = read_doc(app.builder.env.app, app.builder.env, app.builder.env.doc2path(docname))

    # post-processing
    for domain in app.builder.env.domains.values():
        domain.process_doc(app.builder.env, docname, doctree)

    # allow extension-specific post-processing
    if app:
        app.emit('doctree-read', doctree)

print('docnames', docnames)
updated_docnames = set(docnames)
print('updated_docnames:', updated_docnames)

Example #24
0
def get_doctree(path, **kwargs):
    """
    Obtain a Sphinx doctree from the RST file at ``path``.

    Performs no Releases-specific processing; this code would, ideally, be in
    Sphinx itself, but things there are pretty tightly coupled. So we wrote
    this.

    Any additional kwargs are passed unmodified into an internal `make_app`
    call.

    :param str path: A relative or absolute file path string.

    :returns:
        A two-tuple of the generated ``sphinx.application.Sphinx`` app and the
        doctree (a ``docutils.document`` object).

    .. versionchanged:: 1.6
        Added support for passing kwargs to `make_app`.
    """
    root, filename = os.path.split(path)
    docname, _ = os.path.splitext(filename)
    # TODO: this only works for top level changelog files (i.e. ones where
    # their dirname is the project/doc root)
    app = make_app(srcdir=root, **kwargs)
    # Create & init a BuildEnvironment. Mm, tasty side effects.
    app._init_env(freshenv=True)
    env = app.env
    # More arity/API changes: Sphinx 1.3/1.4-ish require one to pass in the app
    # obj in BuildEnvironment.update(); modern Sphinx performs that inside
    # Application._init_env() (which we just called above) and so that kwarg is
    # removed from update(). EAFP.
    kwargs = dict(
        config=app.config,
        srcdir=root,
        doctreedir=app.doctreedir,
        app=app,
    )
    try:
        env.update(**kwargs)
    except TypeError:
        # Assume newer Sphinx w/o an app= kwarg
        del kwargs["app"]
        env.update(**kwargs)
    # Code taken from sphinx.environment.read_doc; easier to manually call
    # it with a working Environment object, instead of doing more random crap
    # to trick the higher up build system into thinking our single changelog
    # document was "updated".
    env.temp_data["docname"] = docname
    env.app = app
    # NOTE: SphinxStandaloneReader API changed in 1.4 :(
    reader_kwargs = {
        "app": app,
        "parsers": env.config.source_parsers,
    }
    if sphinx.version_info[:2] < (1, 4):
        del reader_kwargs["app"]
    # This monkeypatches (!!!) docutils to 'inject' all registered Sphinx
    # domains' roles & so forth. Without this, rendering the doctree lacks
    # almost all Sphinx magic, including things like :ref: and :doc:!
    with sphinx_domains(env):
        try:
            reader = SphinxStandaloneReader(**reader_kwargs)
        except TypeError:
            # If we import from io, this happens automagically, not in API
            del reader_kwargs["parsers"]
            reader = SphinxStandaloneReader(**reader_kwargs)
        pub = Publisher(
            reader=reader, writer=SphinxDummyWriter(), destination_class=NullOutput
        )
        pub.set_components(None, "restructuredtext", None)
        pub.process_programmatic_settings(None, env.settings, None)
        # NOTE: docname derived higher up, from our given path
        src_path = env.doc2path(docname)
        source = SphinxFileInput(
            app,
            env,
            source=None,
            source_path=src_path,
            encoding=env.config.source_encoding,
        )
        pub.source = source
        pub.settings._source = src_path
        pub.set_destination(None, None)
        pub.publish()
        return app, pub.document
Example #25
0
    def read_doc(self, docname, app=None):
        # type: (unicode, Sphinx) -> None
        """Parse a file and add/update inventory entries for the doctree."""

        self.temp_data['docname'] = docname
        # defaults to the global default, but can be re-set in a document
        self.temp_data['default_domain'] = \
            self.domains.get(self.config.primary_domain)

        self.settings['input_encoding'] = self.config.source_encoding
        self.settings['trim_footnote_reference_space'] = \
            self.config.trim_footnote_reference_space
        self.settings['gettext_compact'] = self.config.gettext_compact

        docutilsconf = path.join(self.srcdir, 'docutils.conf')
        # read docutils.conf from source dir, not from current dir
        OptionParser.standard_config_files[1] = docutilsconf
        if path.isfile(docutilsconf):
            self.note_dependency(docutilsconf)

        with sphinx_domains(self):
            if self.config.default_role:
                role_fn, messages = roles.role(self.config.default_role, english,
                                               0, dummy_reporter)
                if role_fn:
                    roles._roles[''] = role_fn
                else:
                    logger.warning('default role %s not found', self.config.default_role,
                                   location=docname)

            codecs.register_error('sphinx', self.warn_and_replace)  # type: ignore

            # publish manually
            reader = SphinxStandaloneReader(self.app, parsers=self.config.source_parsers)
            pub = Publisher(reader=reader,
                            writer=SphinxDummyWriter(),
                            destination_class=NullOutput)
            pub.set_components(None, 'restructuredtext', None)
            pub.process_programmatic_settings(None, self.settings, None)
            src_path = self.doc2path(docname)
            source = SphinxFileInput(app, self, source=None, source_path=src_path,
                                     encoding=self.config.source_encoding)
            pub.source = source
            pub.settings._source = src_path
            pub.set_destination(None, None)
            pub.publish()
            doctree = pub.document

        # post-processing
        for domain in itervalues(self.domains):
            domain.process_doc(self, docname, doctree)

        # allow extension-specific post-processing
        if app:
            app.emit('doctree-read', doctree)

        # store time of reading, for outdated files detection
        # (Some filesystems have coarse timestamp resolution;
        # therefore time.time() can be older than filesystem's timestamp.
        # For example, FAT32 has 2sec timestamp resolution.)
        self.all_docs[docname] = max(
            time.time(), path.getmtime(self.doc2path(docname)))

        if self.versioning_condition:
            old_doctree = None
            if self.versioning_compare:
                # get old doctree
                try:
                    with open(self.doc2path(docname,
                                            self.doctreedir, '.doctree'), 'rb') as f:
                        old_doctree = pickle.load(f)
                except EnvironmentError:
                    pass

            # add uids for versioning
            if not self.versioning_compare or old_doctree is None:
                list(add_uids(doctree, self.versioning_condition))
            else:
                list(merge_doctrees(
                    old_doctree, doctree, self.versioning_condition))

        # make it picklable
        doctree.reporter = None
        doctree.transformer = None
        doctree.settings.warning_stream = None
        doctree.settings.env = None
        doctree.settings.record_dependencies = None

        # cleanup
        self.temp_data.clear()
        self.ref_context.clear()
        roles._roles.pop('', None)  # if a document has set a local default role

        # save the parsed doctree
        doctree_filename = self.doc2path(docname, self.doctreedir,
                                         '.doctree')
        ensuredir(path.dirname(doctree_filename))
        with open(doctree_filename, 'wb') as f:
            pickle.dump(doctree, f, pickle.HIGHEST_PROTOCOL)
Example #26
0
def get_doctree(path, **kwargs):
    """
    Obtain a Sphinx doctree from the RST file at ``path``.

    Performs no Releases-specific processing; this code would, ideally, be in
    Sphinx itself, but things there are pretty tightly coupled. So we wrote
    this.

    Any additional kwargs are passed unmodified into an internal `make_app`
    call.

    :param str path: A relative or absolute file path string.

    :returns:
        A two-tuple of the generated ``sphinx.application.Sphinx`` app and the
        doctree (a ``docutils.document`` object).

    .. versionchanged:: 1.6
        Added support for passing kwargs to `make_app`.
    """
    root, filename = os.path.split(path)
    docname, _ = os.path.splitext(filename)
    # TODO: this only works for top level changelog files (i.e. ones where
    # their dirname is the project/doc root)
    app = make_app(srcdir=root, **kwargs)
    # Create & init a BuildEnvironment. Mm, tasty side effects.
    app._init_env(freshenv=True)
    env = app.env
    # More arity/API changes: Sphinx 1.3/1.4-ish require one to pass in the app
    # obj in BuildEnvironment.update(); modern Sphinx performs that inside
    # Application._init_env() (which we just called above) and so that kwarg is
    # removed from update(). EAFP.
    kwargs = dict(
        config=app.config,
        srcdir=root,
        doctreedir=app.doctreedir,
        app=app,
    )
    try:
        env.update(**kwargs)
    except TypeError:
        # Assume newer Sphinx w/o an app= kwarg
        del kwargs['app']
        env.update(**kwargs)
    # Code taken from sphinx.environment.read_doc; easier to manually call
    # it with a working Environment object, instead of doing more random crap
    # to trick the higher up build system into thinking our single changelog
    # document was "updated".
    env.temp_data['docname'] = docname
    env.app = app
    # NOTE: SphinxStandaloneReader API changed in 1.4 :(
    reader_kwargs = {
        'app': app,
        'parsers': env.config.source_parsers,
    }
    if sphinx.version_info[:2] < (1, 4):
        del reader_kwargs['app']
    # This monkeypatches (!!!) docutils to 'inject' all registered Sphinx
    # domains' roles & so forth. Without this, rendering the doctree lacks
    # almost all Sphinx magic, including things like :ref: and :doc:!
    with sphinx_domains(env):
        reader = SphinxStandaloneReader(**reader_kwargs)
        pub = Publisher(reader=reader,
                        writer=SphinxDummyWriter(),
                        destination_class=NullOutput)
        pub.set_components(None, 'restructuredtext', None)
        pub.process_programmatic_settings(None, env.settings, None)
        # NOTE: docname derived higher up, from our given path
        src_path = env.doc2path(docname)
        source = SphinxFileInput(
            app,
            env,
            source=None,
            source_path=src_path,
            encoding=env.config.source_encoding,
        )
        pub.source = source
        pub.settings._source = src_path
        pub.set_destination(None, None)
        pub.publish()
        return app, pub.document
Example #27
0
def mock_document(tmp_path) -> nodes.document:
    settings = OptionParser(components=(RSTParser, )).get_default_values()
    document = new_document("notset", settings=settings)
    document.settings.env = MockEnv(tmp_path)
    with sphinx_domains(document.settings.env):
        yield document