1# -----------------------------------------------------------------------------
2# Copyright (c) 2012 - 2021, Anaconda, Inc., and Bokeh Contributors.
3# All rights reserved.
4#
5# The full license is in the file LICENSE.txt, distributed with this software.
6# -----------------------------------------------------------------------------
7""" Thoroughly document Bokeh settings.
8
9The ``bokeh-model`` directive will automatically document all the attributes
10(including Bokeh PrioritizedSettings) of an object.
11
12This directive takes the name of a module attribute
13
14.. code-block:: rest
15
16    .. bokeh-settings:: settings
17        :module: bokeh.settings
18
19"""
20
21# -----------------------------------------------------------------------------
22# Boilerplate
23# -----------------------------------------------------------------------------
24import logging  # isort:skip
25
26log = logging.getLogger(__name__)
27
28# -----------------------------------------------------------------------------
29# Imports
30# -----------------------------------------------------------------------------
31
32# Standard library imports
33import importlib
34import textwrap
35
36# External imports
37from docutils.parsers.rst.directives import unchanged
38from sphinx.errors import SphinxError
39
40# Bokeh imports
41from bokeh.settings import PrioritizedSetting, _Unset
42
43# Bokeh imports
44from .bokeh_directive import BokehDirective, py_sig_re
45from .templates import SETTINGS_DETAIL
46
47# -----------------------------------------------------------------------------
48# Globals and constants
49# -----------------------------------------------------------------------------
50
51__all__ = (
52    "BokehSettingsDirective",
53    "setup",
54)
55
56# -----------------------------------------------------------------------------
57# General API
58# -----------------------------------------------------------------------------
59
60# -----------------------------------------------------------------------------
61# Dev API
62# -----------------------------------------------------------------------------
63
64
65class BokehSettingsDirective(BokehDirective):
66
67    has_content = True
68    required_arguments = 1
69    optional_arguments = 1
70    option_spec = {"module": unchanged}
71
72    def run(self):
73        sig = " ".join(self.arguments)
74
75        m = py_sig_re.match(sig)
76        if m is None:
77            raise SphinxError(f"Unable to parse signature for bokeh-model: {sig!r}")
78        name_prefix, obj_name, arglist, retann = m.groups()
79
80        module_name = self.options["module"]
81
82        try:
83            module = importlib.import_module(module_name)
84        except ImportError:
85            raise SphinxError(f"Unable to generate reference docs for {obj_name}: couldn't import module {module_name}")
86
87        obj = getattr(module, obj_name, None)
88        if obj is None:
89            raise SphinxError(f"Unable to generate reference docs for {obj_name}: no model {obj_name} in module {module_name}")
90
91        settings = []
92        for x in obj.__class__.__dict__.values():
93            if not isinstance(x, PrioritizedSetting):
94                continue
95            # help = [line.strip() for line in x.help.strip().split("\n")]
96            setting = {
97                "name": x.name,
98                "env_var": x.env_var,
99                "type": x.convert_type,
100                "help": textwrap.dedent(x.help),
101                "default": "(Unset)" if x.default is _Unset else repr(x.default),
102                "dev_default": "(Unset)" if x.dev_default is _Unset else repr(x.dev_default),
103            }
104            settings.append(setting)
105
106        rst_text = SETTINGS_DETAIL.render(name=obj_name, module_name=module_name, settings=settings)
107        return self._parse(rst_text, "<bokeh-settings>")
108
109
110def setup(app):
111    """ Required Sphinx extension setup function. """
112    app.add_directive_to_domain("py", "bokeh-settings", BokehSettingsDirective)
113
114
115# -----------------------------------------------------------------------------
116# Private API
117# -----------------------------------------------------------------------------
118
119# -----------------------------------------------------------------------------
120# Code
121# -----------------------------------------------------------------------------
122