Skip to content

Latest commit

 

History

History
270 lines (190 loc) · 8.21 KB

File metadata and controls

270 lines (190 loc) · 8.21 KB

Extending

Extending the FDL

You can extend the :doc:`dsl` by registering custom types, flags, and by-options through the :py:attr:`~sphinxnotes.render.Registry.data` attribute of :py:data:`sphinxnotes.render.REGISTRY`.

Adding Custom Types

Use :py:meth:`~sphinxnotes.render.data.REGISTRY.add_type` method of :py:data:`sphinxnotes.render.REGISTRY` to add a new type:

>>> from sphinxnotes.render import REGISTRY, Field
>>>
>>> def parse_color(v: str):
...     return tuple(int(x) for x in v.split(';'))
...
>>> def color_to_str(v):
...     return ';'.join(str(x) for x in v)
...
>>> REGISTRY.data.add_type('color', tuple, parse_color, color_to_str)
>>> Field.from_dsl('color').parse('255;0;0')
(255, 0, 0)

Adding Custom Flags

Use :py:meth:`~sphinxnotes.render.data.Registry.add_flag` method of :py:data:`sphinxnotes.render.REGISTRY` to add a new flag:

>>> from sphinxnotes.render import REGISTRY, Field
>>> REGISTRY.data.add_flag('unique', default=False)
>>> field = Field.from_dsl('int, unique')
>>> field.unique
True

Adding Custom By-Options

Use :py:meth:`~sphinxnotes.render.data.Registry.add_by_option` method of :py:data:`sphinxnotes.render.REGISTRY` to add a new by-option:

>>> from sphinxnotes.render import REGISTRY, Field
>>> REGISTRY.data.add_by_option('group', str)
>>> field = Field.from_dsl('str, group by size')
>>> field.group
'size'
>>> REGISTRY.data.add_by_option('index', str, store='append')
>>> field = Field.from_dsl('str, index by month, index by year')
>>> field.index
['month', 'year']

Extending Extra Contexts

Extra contexts are registered by a :py:deco:`sphinxnotes.render.extra_context` class decorator.

The decorated class must be a subclass of :py:class:`~sphinxnotes.render.ExtraContext`. The generate() method accepts the ExtraContextRequest as the first argument, plus any positional and keyword arguments passed by the template via load_extra().

.. literalinclude:: ../tests/roots/test-extra-context/conf.py
   :language: python
   :start-after: [literalinclude start]
   :end-before: [literalinclude end]

.. dropdown:: :file:`cat.json`

   .. literalinclude:: ../tests/roots/test-extra-context/cat.json

.. example::

   .. data.render::

      {{ load_extra('cat').name }}

To accept custom parameters in your extra context, add *args and **kwargs to the generate() method signature:

.. literalinclude:: ../tests/roots/test-extra-context-params/conf.py
   :language: python
   :start-after: [literalinclude start]
   :end-before: [literalinclude end]

.. example::

   .. data.render::

      {% set docs = load_extra('all_docs', 3) %}
      {% for doc in docs %}
      - :doc:`{{ doc }}`
      {% endfor %}

Extending Filters

Template filters are registered by a :py:deco:`sphinxnotes.render.filter` function decorator.

.. literalinclude:: ../tests/roots/test-filter-example/conf.py
   :language: python
   :start-after: [literalinclude catify-start]
   :end-before: [literalinclude catify-end]

.. example::

   .. data.render::

      {{ "Hello world" | catify }}

If your filter needs access to the Sphinx build environment :py:class:`sphinx.environment.BuildEnvironment` (e.g., to access configuration or document metadata), use pass_build_env=True:

.. literalinclude:: ../tests/roots/test-filter-example/conf.py
   :language: python
   :start-after: [literalinclude author-start]
   :end-before: [literalinclude author-end]

.. example::

   .. data.render::

      {{ "author: Hello world" | format_author }}

Extending Directives/Roles

Tip

Before reading this documentation, please refer to :external+sphinx:doc:`development/tutorials/extending_syntax`. See how to extend :py:class:`SphinxDirective` and :py:class:`SphinxRole`.

All of the classes listed in :ref:`api-directives` are subclassed from the internal sphinxnotes.render.Pipeline class, which is responsible to generate the dedicated :py:class:`node <sphinxnotes.render.pending_node>` that carries a :ref:`context` and a :py:class:`~sphinxnotes.render.Template`.

At the appropriate :ref:`render-phases`, the node will be rendered into markup text, usually reStructuredText. The rendered text is then parsed again by Sphinx and inserted into the document.

.. seealso::

   - :doc:`tmpl` for template variables, phases, and extra context
   - :doc:`dsl` for the field description language used by
     :py:class:`~sphinxnotes.render.Field` and
     :py:class:`~sphinxnotes.render.Schema`
   - Implementations of :parsed_literal:`sphinxnotes-render.ext__`
     and :parsed_literal:`sphinxnotes-any__`.

   __ https://github.com/sphinx-notes/render/tree/master/src/sphinxnotes/render/ext
   __ https://github.com/sphinx-notes/any

Now we have a quick example to help you get Started. :external+sphinx:doc:`Create a Sphinx documentation <tutorial/getting-started>` with the following conf.py:

.. literalinclude:: ../tests/roots/test-base-context-directive-example/conf.py

This is the smallest useful extension built on top of sphinxnotes.render:

Now use the directive in your document:

.. example::

   .. mimi::

BaseDataDefineDirective is higher level of API than BaseContextDirective. You no longer need to implement the current_context methods; instead, implement the :py:meth:`~sphinxnotes.render.BaseDataDefineDirective.current_schema` method.

Here's an example:

.. literalinclude:: ../tests/roots/test-base-data-define-directive-example/conf.py

Key differences from BaseContextDirective:

Use the directive in your document:

.. example::

   .. cat2:: mimi
      :color: black and brown
      :birth: 2025

      I like fish!

StrictDataDefineDirective is an even higher-level API built on top of BaseDataDefineDirective. It automatically handles SphinxDirective's members from your :py:class:`~sphinxnotes.render.Schema`, so you don't need to manually set:

  • required_arguments / optional_arguments - derived from Schema.name
  • option_spec - derived from Schema.attrs
  • has_content - derived from Schema.content

You no longer need to manually create subclasses, simply pass schema and template to :py:meth:`~sphinxnotes.render.StrictDataDefineDirective.derive` method:

.. literalinclude:: ../tests/roots/test-strict-data-define-directive-example/conf.py

Use the directive in your document:

.. example::

   .. cat3:: mimi
      :color: black and brown
      :birth: 2025

      I like fish!