33import json
44import os
55from pathlib import Path
6+ from textwrap import dedent
67
78from docutils .parsers .rst import Directive , directives
89from docutils import nodes
1213
1314logger = logging .getLogger (__name__ )
1415
15- THEBE_VERSION = "0.5.1 "
16+ THEBE_VERSION = "0.8.2 "
1617
1718
1819def st_static_path (app ):
@@ -21,16 +22,22 @@ def st_static_path(app):
2122
2223
2324def init_thebe_default_config (app , env , docnames ):
25+ """Create a default config for fields that aren't given by the user."""
2426 thebe_config = app .config .thebe_config
2527 defaults = {
26- "always_load" : True ,
28+ "always_load" : False ,
2729 "selector" : ".thebe" ,
2830 "selector_input" : "pre" ,
2931 "selector_output" : ".output" ,
3032 }
3133 for key , val in defaults .items ():
3234 if key not in thebe_config :
3335 thebe_config [key ] = val
36+
37+ # Standardize types for certain values
38+ BOOL_KEYS = ["always_load" ]
39+ for key in BOOL_KEYS :
40+ thebe_config [key ] = _bool (thebe_config [key ])
3441
3542
3643def _bool (b ):
@@ -42,46 +49,45 @@ def _bool(b):
4249
4350def _do_load_thebe (doctree , config_thebe ):
4451 """Decide whether to load thebe based on the page's context."""
52+ # No doctree means there's no page content at all
4553 if not doctree :
4654 return False
4755
4856 # If we aren't properly configured
4957 if not config_thebe :
50- logger .warning ("Didn't find `thebe_config` in conf.py, add to use thebe" )
58+ logger .warning ("[sphinx-thebe]: Didn't find `thebe_config` in conf.py, add to use thebe" )
5159 return False
60+
61+ return True
5262
53- # Only load `thebe` if there is a thebe button somewhere
54- if doctree .traverse (ThebeButtonNode ) or _bool (config_thebe .get ("always_load" )):
55- return True
56- else :
57- return False
5863
59-
60- def init_thebe_core (app , pagename , templatename , context , doctree ):
61- """Load thebe assets if there's a thebe button on this page."""
64+ def init_thebe_core (app , env , docnames ):
65+ """Add scripts to configure thebe, and optionally add thebe itself.
66+
67+ By default, defer loading the `thebe` JS bundle until bootstrap is called
68+ in order to speed up page load times.
69+ """
6270 config_thebe = app .config ["thebe_config" ]
63- if not _do_load_thebe (doctree , config_thebe ):
64- return
65-
66- # Add core libraries
67- opts = {"async" : "async" }
68- app .add_js_file (
69- filename = f"https://unpkg.com/thebe@{ THEBE_VERSION } /lib/index.js" , ** opts
70- )
7171
7272 # Add configuration variables
73- thebe_config = f"""
73+ THEBE_JS_URL = f"https://unpkg.com/thebe@{ THEBE_VERSION } /lib/index.js"
74+ thebe_config = f"""\
75+ const THEBE_JS_URL = "{ THEBE_JS_URL } "
7476 const thebe_selector = "{ app .config .thebe_config ['selector' ] } "
7577 const thebe_selector_input = "{ app .config .thebe_config ['selector_input' ] } "
7678 const thebe_selector_output = "{ app .config .thebe_config ['selector_output' ] } "
7779 """
78- app .add_js_file (None , body = thebe_config )
79- app .add_js_file (filename = "sphinx-thebe.js" , ** opts )
80+ app .add_js_file (None , body = dedent ( thebe_config ) )
81+ app .add_js_file (filename = "sphinx-thebe.js" , ** { "async" : "async" } )
8082
83+ if config_thebe .get ("always_load" ) is True :
84+ # If we've got `always load` on, then load thebe on every page.
85+ app .add_js_file (THEBE_JS_URL , ** {"async" : "async" })
8186
8287def update_thebe_context (app , doctree , docname ):
83- """Add thebe config nodes to this doctree."""
88+ """Add thebe config nodes to this doctree using page-dependent information ."""
8489 config_thebe = app .config ["thebe_config" ]
90+ # Skip modifying the doctree if we don't need to load thebe
8591 if not _do_load_thebe (doctree , config_thebe ):
8692 return
8793
@@ -94,7 +100,6 @@ def update_thebe_context(app, doctree, docname):
94100 )
95101 codemirror_theme = config_thebe .get ("codemirror-theme" , "abcdef" )
96102
97- # Thebe configuration
98103 # Choose the kernel we'll use
99104 meta = app .env .metadata .get (docname , {})
100105 kernel_name = meta .get ("thebe-kernel" )
@@ -142,6 +147,7 @@ def update_thebe_context(app, doctree, docname):
142147 </script>
143148 """
144149
150+ # Append to the docutils doctree so it makes it into the build outputs
145151 doctree .append (nodes .raw (text = thebe_html_config , format = "html" ))
146152 doctree .append (
147153 nodes .raw (text = f"<script>kernelName = '{ kernel_name } '</script>" , format = "html" )
@@ -154,7 +160,7 @@ def _split_repo_url(url):
154160 end = url .split ("github.com/" )[- 1 ]
155161 org , repo = end .split ("/" )[:2 ]
156162 else :
157- logger .warning (f"Currently Thebe repositories must be on GitHub, got { url } " )
163+ logger .warning (f"[sphinx-thebe]: Currently Thebe repositories must be on GitHub, got { url } " )
158164 org = repo = None
159165 return org , repo
160166
@@ -220,12 +226,12 @@ def setup(app):
220226 # Set default values for the configuration
221227 app .connect ("env-before-read-docs" , init_thebe_default_config )
222228
229+ # Load the JS/CSS assets for thebe if needed
230+ app .connect ("env-before-read-docs" , init_thebe_core )
231+
223232 # Update the doctree with thebe-specific information if needed
224233 app .connect ("doctree-resolved" , update_thebe_context )
225234
226- # Load the JS/CSS assets for thebe if needed
227- app .connect ("html-page-context" , init_thebe_core )
228-
229235 # configuration for this tool
230236 app .add_config_value ("thebe_config" , {}, "html" )
231237
0 commit comments