Skip to content
This repository was archived by the owner on Jul 21, 2021. It is now read-only.

Conversation

@gabrielfalcao
Copy link

@gabrielfalcao gabrielfalcao commented Oct 16, 2018

This is an attempt to make sphinx even more extensible.


  • add event "upgrade-node"
  • introduce new methods to application.Sphinx
    • create_node - to create nodes that can be modified in runtime by listener callbacks
    • upgrade_node - upgrades a newly created node
  • replace all occurrences of addnodes.only() with app.create_node(addnodes.only)
  • introduce function sphinx.util.nodes.subclasses_of
  • introduce function sphinx.util.nodes.traverse_subclasses_of
  • replace all occurrences of document.traverse(addnodes.only) with traverse_subclasses_of(addnodes.only, document)

Extension used for testing concept

# fmt: off
import logging

from docutils import nodes

from sphinx import addnodes

from sphinx.directives.other import Only

from sphinx.transforms import SphinxTransform
from sphinx.transforms.post_transforms import OnlyNodeTransform

from sphinx.util.docutils import SphinxDirective
from sphinx.util.nodes import process_only_nodes
from sphinx.util.nodes import set_source_info

version = '0.0.1'
logger = logging.getLogger(__name__)


def wrap_children_in_container(node, css_class_name):
    wrappernode = nodes.container(classes=[css_class_name])
    wrappernode.extend(node.children)
    node.replace_self(wrappernode)

def process_conditional_output_nodes(document, tags):
    """similar to :py:func:`~sphinx.uti.nodes.process_only_nodes` but
    wraps children in a container and adds a css class for styling.
    """
    for node in document.traverse(conditional_output):
        css_class_name = node['expr']
        try:
            ret = tags.eval_condition(css_class_name)
        except Exception as err:
            self.wrap_children_in_container(node, css_class_name)

        else:
            if ret:
                wrap_children_in_container(node, css_class_name)
            else:
                # A comment on the comment() nodes being inserted: replacing by [] would
                # result in a "Losing ids" exception if there is a target node before
                # the only node, so we make sure docutils can transfer the id to
                # something, even if it's just a comment and will lose the id anyway...
                node.replace_self(nodes.comment())


class conditional_output(nodes.General, nodes.Element):
    pass



class ConditionalOutput(SphinxDirective):
    """pretty much a clone of :py:class:`sphinx.directive.other.Only`
    """
    has_content = True
    required_arguments = 1
    optional_arguments = 0
    final_argument_whitespace = True
    option_spec = {}

    only_node_class = conditional_output

    def run(self):
        # self.app.events.connect('upgrade-node', self.upgrade_node)
        return [conditional_output()]

    def upgrade_node(self, current_version, previous_version, sender):
        if isinstance(sender, Only):
            import ipdb;ipdb.set_trace()

        # if sender == self and isinstance(current_version, addnodes.only):
        #     return current_version

        # if not isinstance(sender, Only) and not isinstance(current_version, addnodes.only):
        #     return current_version

        # import ipdb;ipdb.set_trace()
        # node = self.app.create_node(addnodes.conditional_output, expr=self.arguments[0])
        # return node

class ConditionalOutputTransformer(OnlyNodeTransform):
    """This is pretty much a copy of

    :py:func:`sphinx.util.nodes.process_only_nodes` except it wraps all
    children in a common container and the code is cleaner :)

    """
    default_priority = OnlyNodeTransform.default_priority - 10

    def apply(self):
        self.app.events.connect('upgrade-node', self.upgrade_node)
        super(ConditionalOutputTransformer, self).apply()

    def upgrade_node(self, current_version, previous_version, sender=None):
        if isinstance(sender, Only):
            import ipdb;ipdb.set_trace()

        # if current_version == previous_version:
        #     return None

        # import ipdb;ipdb.set_trace()
        # if sender == self and isinstance(current_version, addnodes.only):
        #     import ipdb;ipdb.set_trace()
        #     return None

        # if not isinstance(sender, Only) and not isinstance(current_version, addnodes.only):
        #     return None

        # if isinstance(current_version, conditional_output):
        #     process_conditional_output_nodes(self.document, self.app.builder.tags)


def setup(app):
    app.add_node(conditional_output)
    app.add_directive("conditional_output", ConditionalOutput)
    app.add_transform(ConditionalOutputTransformer)

    return {
        'version': version,
        'parallel_read_safe': True,
        'parallel_write_safe': True,
    }

- add event "upgrade-node"
- introduce new methods to ``application.Sphinx``
  - ``create_node`` - to create nodes that can be modified in runtime by listener callbacks
  - ``upgrade_node`` - upgrades a newly created node
- replace all occurrences of ``addnodes.only()`` with ``app.create_node(addnodes.only)``
- introduce function ``sphinx.util.nodes.subclasses_of``
- introduce function ``sphinx.util.nodes.traverse_subclasses_of```
- replace all occurrences of ```document.traverse(addnodes.only)`` with ``traverse_subclasses_of(addnodes.only, document)``
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants