Discussion:
Nested directives and statemachine
(too old to reply)
Daniel Woste
2017-08-29 07:02:43 UTC
Permalink
Hi all,

What is the best way to handle nested directives inside a sphinx plugin?

My use case:
I have a directive ".. bug::", which creates a paragraph with title,
status, tags and final some content, which can contain other directives
like ".. note::".
I have builder "bugs", which exports all found bugs to a json file.
And finally I have an import directive ".. bugimport", which reads in an
exported json file and for each found entry it creates a ".. bug::"
directive.

So the directive flow is something like: 1x bugimport --> n x bug --> n x
any other directive (part of the bug content).

The only technique I know for getting nested directives rendered is by
adding content directly to the state_machine:
self.state_machine.insert_input(my_content, s
elf.state_machine.document.attributes['source'])

If I try this inside my ".. bugimport::" directive, each ".. bug::"
directive gets executed twice, which produces some major problems.

Are there any other techniques I can use to place and render new "content"
to an document?
Maybe I can use the bug-node directly, but I'm not sure, if I'm able to set
needed options or if the nested directives get rendered.

If you ask yourself for what kind of plugin this is needed, feel free to
visit http://sphinxcontrib-needs.readthedocs.io

Any advice is highly welcome,
Daniel
--
You received this message because you are subscribed to the Google Groups "sphinx-dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email to sphinx-dev+***@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
Peter Burdine
2017-08-31 14:54:18 UTC
Permalink
If you want Sphinx to parse nested directives/nodes, you need to tell it to
do so:

node = nodes.Element()
node.document = self.state.document
sphinx.util.nodes.nested_parse_with_titles(self.state,
ViewList(new_content.
splitlines(), source=docname),
node)
return node.children

Where new_content is the text you want sphinx to parse.
Post by Daniel Woste
What is the best way to handle nested directives inside a sphinx plugin?
I have a directive ".. bug::", which creates a paragraph with title,
status, tags and final some content, which can contain other directives
like ".. note::".
I have builder "bugs", which exports all found bugs to a json file.
Does this mean the directive content is parsed into a doctree node of
class "bug" and then "pickled" as json?
Is the content of the directive parsed at this stage?
Post by Daniel Woste
And finally I have an import directive ".. bugimport", which reads in an
exported json file and for each found entry it creates a ".. bug::"
directive.
So the directive flow is something like: 1x bugimport --> n x bug --> n
x
Post by Daniel Woste
any other directive (part of the bug content).
The only technique I know for getting nested directives rendered is by
self.state_machine.insert_input(my_content, s
elf.state_machine.document.attributes['source'])
If I try this inside my ".. bugimport::" directive, each ".. bug::"
directive gets executed twice, which produces some major problems.
Are there any other techniques I can use to place and render new
"content"
Post by Daniel Woste
to an document?
Have a look how the Docutils source handles directives that add content to
the doctree.
One possibility that comes to my mind is storing the raw rst in the
exported
file(s) and call an "include" directive class instance in "bugimport".
Post by Daniel Woste
Maybe I can use the bug-node directly, but I'm not sure, if I'm able to
set
Post by Daniel Woste
needed options or if the nested directives get rendered.
Have a look at the transforms in the Docutils source. Transforms take
parts
of the doctree, process it and modify or add nodes at other places in
the doctree.
GÃŒnter
--
You received this message because you are subscribed to the Google Groups "sphinx-dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email to sphinx-dev+***@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
Rob van der Valk
2017-09-15 13:29:20 UTC
Permalink
I am using slightly different code then Peter's, for 2 reasons:
- I understand per Sphinx manual that Directive is the preferred method.
Simplifies code a bit.
- I would always consider returning node instead of the children only: in
current node you may still want to have info for later phases like the
writer, such as scanned options. The standard writer will visit all
nodes and act on the related node type with its associated actions. To
show this in code:

From my __init__.py:

def setup(app):

app.add_directive('popup', popupDirective)
app.add_role('popup',popupRole)
app.add_node(popupNode,html=(visitPopupNode, departPopupNode))
extraCss = _find("popupExt/static/css/popup.css")
app.add_stylesheet(extraCss)


and from my actual Directive:

from docutils.nodes import Element, General
from docutils.parsers.rst import Directive, directives
from sphinx.util.nodes import nested_parse_with_titles


class popupNode(General, Element):
pass
class popupDirective(Directive): # std from docutils/Sphinx: define a
bunch of options and use it
has_content = True
required_arguments = 0
optional_arguments = 1
final_argument_whitespace = True
option_spec = {
"color" : directives.unchanged,
'refname' : directives.unchanged_required,
'tag' : directives.unchanged_required
}

def run(self): # called when scanning the file
emptyNode = popupNode()
emptyNode['nullNode'] = True

node = popupNode()
node['nullNode'] = False

node.document = self.state.document
#node.env = self.state.document.settings.env

if len(self.arguments) == 0:
return[self.state.document.reporter.warning('Missing tag in popup
directive', line=self.lineno)]
node['tag'] = self.arguments[0]
node['serialNr'] = self.state.document.settings.env.new_serialno(
"popup")
if 'refname' not in self.options:
self.state.document.reporter.warning('Missing refname in popup
directive', line=self.lineno)
node['refName'] = 'Click me!'
else:
node['refName'] = self.options.get("refname")
if 'color' in self.options:
node['color'] = self.options.get("color")

self.state.nested_parse(self.content, self.content_offset,node) #
make sure the text portion is handled as ReST

popupStore(self.state.document).add(node,self.lineno) # store the
node on doc level. Used again in popupRole

return [emptyNode]


My structure is aimed at having somewhere jotted down some short popup note
with a directive (very handy in my work as chip architect) but be able to
get that inline (with a Sphinx role) somewhere in the text. I store the
whole lot into the local environment (in my case not env but document, I
forget those popup notes when leaving a .rst file) and have the pre and
post visits defined properly. The emptyNode contains no data and is thus
left as empty, the role (not included in example) does push in the proper
text. So my definition remains neatly within my solution boundaries yet can
take all stuff along. I think your problem is quite look-a-like.

Rob
--
You received this message because you are subscribed to the Google Groups "sphinx-dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email to sphinx-dev+***@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
Loading...