-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add a directives for general purpose documentation in downstream proj…
…ects
- Loading branch information
1 parent
24e287e
commit 40f9e8f
Showing
12 changed files
with
371 additions
and
75 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
103 changes: 103 additions & 0 deletions
103
django_setup_configuration/documentation/setup_config_usage.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,103 @@ | ||
from dataclasses import dataclass | ||
from pathlib import Path | ||
|
||
from django.template import Template | ||
from django.template.context import Context | ||
from django.utils.module_loading import import_string | ||
from django.utils.text import slugify | ||
|
||
from docutils import nodes | ||
from docutils.parsers.rst import Directive | ||
from docutils.statemachine import ViewList | ||
|
||
from django_setup_configuration.configuration import BaseConfigurationStep | ||
|
||
_TEMPLATES_PATH = Path(__file__).parent / "templates" | ||
|
||
|
||
@dataclass(frozen=True) | ||
class StepInfo: | ||
title: str | ||
anchor_id: str | ||
module_path: str | ||
step_cls: BaseConfigurationStep | ||
|
||
|
||
class SetupConfigUsageDirective(Directive): | ||
has_content = True | ||
|
||
@classmethod | ||
def _get_django_settings(cls): | ||
from django.conf import settings | ||
from django.core.exceptions import AppRegistryNotReady, ImproperlyConfigured | ||
|
||
try: | ||
return settings._wrapped.__dict__ if hasattr(settings, "_wrapped") else {} | ||
except (AppRegistryNotReady, AttributeError, ImproperlyConfigured): | ||
return {} | ||
|
||
def run(self): | ||
if not (settings := self._get_django_settings()): | ||
raise ValueError( | ||
"Unable to load Django settings. Is DJANGO_SETTINGS_MODULE set?" | ||
) | ||
|
||
if not (configured_steps := settings.get("SETUP_CONFIGURATION_STEPS")): | ||
raise ValueError( | ||
"No steps configured. Set SETUP_CONFIGURATION_STEPS in your " | ||
" via your Django settings." | ||
) | ||
|
||
_usage_template = Template((_TEMPLATES_PATH / "config_doc.rst").read_text()) | ||
_step_template = Template((_TEMPLATES_PATH / "config_step.rst").read_text()) | ||
|
||
steps_info = [] | ||
for step_path in configured_steps: | ||
step_cls = import_string(step_path) | ||
step_info = StepInfo( | ||
title=step_cls.verbose_name, | ||
anchor_id=slugify(step_cls.verbose_name), | ||
module_path=step_path, | ||
step_cls=step_cls, | ||
) | ||
steps_info.append(step_info) | ||
|
||
usage_rendered = _usage_template.render(context=Context({"steps": steps_info})) | ||
|
||
rst = ViewList() | ||
rst.append("", "<dynamic>") | ||
|
||
lines = usage_rendered.split("\n") | ||
for line in lines: | ||
rst.append(line, "<dynamic>") | ||
|
||
section_node = nodes.section() | ||
section_node["ids"] = ["django-setup-config"] | ||
section_node += nodes.title( | ||
text="Setting up your application with django-setup-config" | ||
) | ||
self.state.nested_parse(rst, 0, section_node) | ||
|
||
for step in steps_info: | ||
rst = ViewList() | ||
subsection_node = nodes.section(ids=[step.anchor_id]) | ||
subsection_node += nodes.title(text=step.title) | ||
|
||
text = _step_template.render(context=Context({"step": step})) | ||
lines = text.split("\n") | ||
|
||
rst.append("", "<dynamic>") # Blank line | ||
for line in lines: | ||
rst.append(line, "<dynamic>") | ||
|
||
self.state.nested_parse(rst, 0, subsection_node) | ||
section_node += subsection_node | ||
|
||
# self.state.nested_parse(rst, 0, section_node) | ||
return [section_node] | ||
|
||
|
||
def setup(app): | ||
app.add_directive("setup-config-usage", SetupConfigUsageDirective) | ||
|
||
return {"version": "1.0", "parallel_read_safe": True} |
35 changes: 35 additions & 0 deletions
35
django_setup_configuration/documentation/templates/config_doc.rst
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
You can use the included ``setup_configuration`` management command to configure your | ||
instance from a yaml file as follows: | ||
|
||
.. code-block:: bash | ||
python manage.py setup_configuration --yaml-file /path/to/config.yaml | ||
You can also validate that the configuration source can be successfully loaded, | ||
without actually running the steps, by adding the ``validate-only`` flag: | ||
|
||
.. code-block:: bash | ||
python manage.py setup_configuration --yaml-file /path/to/config.yaml --validate-only | ||
Both commands will either return 0 and a success message if the configuration file can | ||
be loaded without issues, otherwise it will return a non-zero exit code and print any | ||
validation errors. | ||
|
||
Your YAML file should contain both a flag indicating whether the step is enabled or | ||
disabled, as well as an object containing the actual configuration values under the | ||
appropriate key. | ||
|
||
.. note:: All steps are disabled by default. You only have to explicitly include the | ||
flag to enable a step, not to disable it, though you may do so if you wish to | ||
have an explicit record of what steps are disabled. | ||
|
||
Further information can be found at the `django-setup-configuration | ||
<https://github.com/maykinmedia/django-setup-configuration/>`_ project page. | ||
|
||
This projects includes the following configuration steps (click on each step for a | ||
brief descripion and an example YAML you can include in your config file): | ||
|
||
{% for step in steps %} | ||
- `{{ step.title }} <#{{ step.anchor_id }}>`_ | ||
{% endfor %} |
4 changes: 4 additions & 0 deletions
4
django_setup_configuration/documentation/templates/config_step.rst
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
.. autoclass:: {{ step.module_path }} | ||
:noindex: | ||
|
||
.. setup-config-example:: {{ step.module_path }} |
50 changes: 0 additions & 50 deletions
50
django_setup_configuration/templates/django_setup_configuration/config_doc.rst
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,39 +1,85 @@ | ||
.. _config_docs: | ||
|
||
Configuration documentation | ||
Configuration Documentation | ||
=========================== | ||
|
||
The library provides a Sphinx directive that generates (and validates) an example configuration | ||
file in YAML format for a given ``ConfigurationStep``, containing information about the names of the fields, | ||
possible values, default values, and a short description. This helps clients determine | ||
what can be configured with the help of the library and how. | ||
The library provides two Sphinx directives: | ||
|
||
1. ``setup-config-example`` - Generates (and validates) an example configuration file in YAML format for a given ``ConfigurationStep``. This includes information about field names, possible values, default values, and descriptions, helping clients understand available configuration options. | ||
|
||
Setup | ||
""""" | ||
2. ``setup-config-usage`` - Generates basic usage information and lists all configured steps with metadata and example YAMLs. This provides a complete overview for users who want to bootstrap their installation. | ||
|
||
Start by adding the following extension to ``conf.py`` in the documentation directory: | ||
Using setup-config-example | ||
-------------------------- | ||
|
||
:: | ||
First, add the extension and its requirements to ``conf.py`` in your documentation directory: | ||
|
||
.. code-block:: python | ||
extensions = [ | ||
... | ||
"sphinx.ext.autodoc", | ||
"django_setup_configuration.documentation.setup_config_example", | ||
... | ||
] | ||
And then display a YAML example by using the directive: | ||
:: | ||
Then display a YAML example using the directive: | ||
|
||
.. code-block:: rst | ||
.. setup-config-example:: path.to.your.ConfigurationStep | ||
which will produce something like the following example (in the case of the ``SitesConfigurationStep`` provided by this library): | ||
This will produce output similar to the following example (using the ``SitesConfigurationStep`` provided by this library): | ||
|
||
.. setup-config-example:: django_setup_configuration.contrib.sites.steps.SitesConfigurationStep | ||
|
||
.. warning:: | ||
|
||
Not all possible configurations are supported by this directive currently. | ||
More complex type annotations like ``list[ComplexObject | ComplexObject]`` will raise errors when | ||
trying to build the documentation | ||
Not all configurations are currently supported by this directive. | ||
Complex type annotations like ``list[ComplexObject | ComplexObject]`` will raise errors during documentation build. | ||
|
||
Using setup-config-usage | ||
------------------------ | ||
|
||
First, add the extension and its requirements to ``conf.py`` in your documentation directory: | ||
|
||
.. code-block:: python | ||
extensions = [ | ||
... | ||
"sphinx.ext.autodoc", | ||
"django_setup_configuration.documentation.setup_config_example", | ||
"django_setup_configuration.documentation.setup_config_usage", | ||
... | ||
] | ||
To use this directive, you'll also have to ensure Django is configured and initialized | ||
in your Sphinx `conf.py` file, for instance like this: | ||
|
||
.. code-block:: python | ||
# docs/conf.py | ||
# ... | ||
import django | ||
from django.conf import settings | ||
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "your_settings_module") | ||
django.setup() | ||
# ... | ||
# extensions = [...] | ||
Then display usage information using the directive: | ||
|
||
.. code-block:: rst | ||
.. setup-config-usage:: | ||
This generates a "how to" introduction for invoking the management command, followed by sections for each configured step with expected YAML configurations. | ||
|
||
.. note:: | ||
|
||
For clarity, create a separate RST file (e.g., ``setup-configuration.rst``) containing only this directive. | ||
Sphinx will include the subsections for each step in your documentation's navigation area. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
<section id="django-setup-config"> | ||
<h1>Setting up your application with django-setup-config<a class="headerlink" href="#django-setup-config" title="Link to this heading">¶</a></h1> | ||
<p>You can use the included <code class="docutils literal notranslate"><span class="pre">setup_configuration</span></code> management command to configure your | ||
instance from a yaml file as follows:</p> | ||
<div class="highlight-bash notranslate"><div class="highlight"><pre><span></span>python<span class="w"> </span>manage.py<span class="w"> </span>setup_configuration<span class="w"> </span>--yaml-file<span class="w"> </span>/path/to/config.yaml | ||
</pre></div> | ||
</div> | ||
<p>You can also validate that the configuration source can be successfully loaded, | ||
without actually running the steps, by adding the <code class="docutils literal notranslate"><span class="pre">validate-only</span></code> flag:</p> | ||
<div class="highlight-bash notranslate"><div class="highlight"><pre><span></span>python<span class="w"> </span>manage.py<span class="w"> </span>setup_configuration<span class="w"> </span>--yaml-file<span class="w"> </span>/path/to/config.yaml<span class="w"> </span>--validate-only | ||
</pre></div> | ||
</div> | ||
<p>Both commands will either return 0 and a success message if the configuration file can | ||
be loaded without issues, otherwise it will return a non-zero exit code and print any | ||
validation errors.</p> | ||
<p>Your YAML file should contain both a flag indicating whether the step is enabled or | ||
disabled, as well as an object containing the actual configuration values under the | ||
appropriate key.</p> | ||
<div class="admonition note"> | ||
<p class="admonition-title">Note</p> | ||
<p>All steps are disabled by default. You only have to explicitly include the | ||
flag to enable a step, not to disable it, though you may do so if you wish to | ||
have an explicit record of what steps are disabled.</p> | ||
</div> | ||
<p>Further information can be found at the <a class="reference external" href="https://github.com/maykinmedia/django-setup-configuration/">django-setup-configuration</a> project page.</p> | ||
<p>This projects includes the following configuration steps (click on each step for a | ||
brief descripion and an example YAML you can include in your config file):</p> | ||
<ul class="simple"> | ||
<li><p><a class="reference external" href="#user-configuration">User Configuration</a></p></li> | ||
</ul> | ||
<section id="user-configuration"> | ||
<h2>User Configuration<a class="headerlink" href="#user-configuration" title="Link to this heading">¶</a></h2> | ||
<dl class="py class"> | ||
<dt class="sig sig-object py"> | ||
<em class="property"><span class="pre">class</span><span class="w"> </span></em><span class="sig-prename descclassname"><span class="pre">testapp.configuration.</span></span><span class="sig-name descname"><span class="pre">UserConfigurationStep</span></span></dt> | ||
<dd><p>Set up an initial user.</p> | ||
</dd></dl> | ||
|
||
<div class="highlight-yaml notranslate"><div class="highlight"><pre><span></span><span class="nt">user_configuration_enabled</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">true</span> | ||
<span class="nt">user_configuration</span><span class="p">:</span> | ||
|
||
<span class="w"> </span><span class="c1"># DESCRIPTION: Required. 150 characters or fewer. Letters, digits and @/./+/-/_</span> | ||
<span class="w"> </span><span class="c1"># only.</span> | ||
<span class="w"> </span><span class="c1"># REQUIRED: true</span> | ||
<span class="w"> </span><span class="nt">username</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">example_string</span> | ||
|
||
<span class="w"> </span><span class="c1"># REQUIRED: true</span> | ||
<span class="w"> </span><span class="nt">password</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">example_string</span> | ||
</pre></div> | ||
</div> | ||
</section> | ||
</section> |
Oops, something went wrong.