class BokehPlotDirective(Directive): has_content = True optional_arguments = 2 option_spec = { 'source-position': lambda x: choice(x, ('below', 'above', 'none')), 'linenos': lambda x: True if flag(x) is None else False, } def run(self): env = self.state.document.settings.env # filename *or* python code content, but not both if self.arguments and self.content: raise SphinxError("bokeh-plot:: directive can't have both args and content") # need docname not to look like a path docname = env.docname.replace("/", "-") if self.content: log.debug("[bokeh-plot] handling inline example in %r", env.docname) path = env.bokeh_plot_auxdir # code runner just needs any real path source = '\n'.join(self.content) else: log.debug("[bokeh-plot] handling external example in %r: %s", env.docname, self.arguments[0]) path = self.arguments[0] if not path.startswith("/"): path = join(env.app.srcdir, path) source = open(path).read() js_name = "bokeh-plot-%s-external-%s.js" % (uuid4().hex, docname) try: (script, js, js_path, source) = _process_script(source, path, env, js_name) except Exception as e: raise RuntimeError("Sphinx bokeh-plot exception: \n\n%s\n\n Failed on:\n\n %s" % (e,source)) env.bokeh_plot_files[js_name] = (script, js, js_path, source, dirname(env.docname)) # use the source file name to construct a friendly target_id target_id = "%s.%s" % (env.docname, basename(js_path)) target = nodes.target('', '', ids=[target_id]) result = [target] linenos = self.options.get('linenos', False) code = nodes.literal_block(source, source, language="python", linenos=linenos, classes=[]) set_source_info(self, code) source_position = self.options.get('source-position', 'below') if source_position == "above": result += [code] result += [nodes.raw('', script, format="html")] if source_position == "below": result += [code] return result
class BokehPlotDirective(Directive): has_content = True optional_arguments = 2 option_spec = { 'source-position': lambda x: choice(x, ('below', 'above', 'none')), 'linenos': lambda x: True if flag(x) is None else False, } def run(self): env = self.state.document.settings.env app = env.app # filename *or* python code content, but not both if self.arguments and self.content: raise SphinxError( "bokeh-plot:: directive can't have both args and content") # process inline examples here if self.content: app.debug("[bokeh-plot] handling inline example in %r", env.docname) source = '\n'.join(self.content) # need docname not to look like a path docname = env.docname.replace("/", "-") serialno = env.new_serialno(env.docname) js_name = "bokeh-plot-%s-inline-%d.js" % (docname, serialno) # the code runner just needs a real path to cd to, this will do path = join(env.bokeh_plot_auxdir, js_name) (script, js, js_path, source) = _process_script(source, path, env.bokeh_plot_auxdir, js_name) env.bokeh_plot_files[js_name] = (script, js, js_path, source) # process example files here else: example_path = self.arguments[0][:-3] # remove the ".py" # if it's an "internal" example, the python parser has already handled it if example_path in env.bokeh_plot_files: app.debug("[bokeh-plot] handling internal example in %r: %s", env.docname, self.arguments[0]) (script, js, js_path, source) = env.bokeh_plot_files[example_path] # handle examples external to the docs source, e.g. gallery examples else: app.debug("[bokeh-plot] handling external example in %r: %s", env.docname, self.arguments[0]) source = open(self.arguments[0]).read() source = decode_utf8(source) docname = env.docname.replace("/", "-") serialno = env.new_serialno(env.docname) js_name = "bokeh-plot-%s-external-%d.js" % (docname, serialno) (script, js, js_path, source) = _process_script(source, self.arguments[0], env.bokeh_plot_auxdir, js_name) env.bokeh_plot_files[js_name] = (script, js, js_path, source) # use the source file name to construct a friendly target_id target_id = "%s.%s" % (env.docname, basename(js_path)) target = nodes.target('', '', ids=[target_id]) result = [target] linenos = self.options.get('linenos', False) code = nodes.literal_block(source, source, language="python", linenos=linenos, classes=[]) set_source_info(self, code) source_position = self.options.get('source-position', 'below') if source_position == "above": result += [code] result += [nodes.raw('', script, format="html")] if source_position == "below": result += [code] return result
class BokehPlotDirective(BokehDirective): has_content = True optional_arguments = 2 option_spec = { "process-docstring": lambda x: flag(x) is None, "source-position": lambda x: choice(x, ("below", "above", "none")), "linenos": lambda x: flag(x) is None, } def run(self): source, path = self.process_args_or_content() dashed_docname = self.env.docname.replace("/", "-") js_filename = f"bokeh-content-{uuid4().hex}-{dashed_docname}.js" try: (script_tag, js_path, source, docstring, height_hint) = self.process_source(source, path, js_filename) except Exception as e: raise SphinxError(f"Error generating {js_filename}: \n\n{e}") self.env.bokeh_plot_files.add((js_path, dirname(self.env.docname))) # use the source file name to construct a friendly target_id target_id = f"{dashed_docname}.{basename(js_path)}" target = [nodes.target("", "", ids=[target_id])] process_docstring = self.options.get("process-docstring", False) intro = self.parse( docstring, '<bokeh-content>') if docstring and process_docstring else [] above, below = self.process_code_block(source, docstring) autoload = [ autoload_script(height_hint=height_hint, script_tag=script_tag) ] return target + intro + above + autoload + below def process_code_block(self, source: str, docstring: str | None): source_position = self.options.get("source-position", "below") if source_position == "none": return [], [] source = _remove_module_docstring(source, docstring).strip() linenos = self.options.get("linenos", False) code_block = nodes.literal_block(source, source, language="python", linenos=linenos, classes=[]) set_source_info(self, code_block) if source_position == "above": return [code_block], [] if source_position == "below": return [], [code_block] def process_args_or_content(self): # filename *or* python code content, but not both if self.arguments and self.content: raise SphinxError( "bokeh-plot:: directive can't have both args and content") if self.content: log.debug( f"[bokeh-plot] handling inline content in {self.env.docname!r}" ) path = self.env.bokeh_plot_auxdir # code runner just needs any real path return "\n".join(self.content), path path = self.arguments[0] log.debug( f"[bokeh-plot] handling external content in {self.env.docname!r}: {path}" ) if not path.startswith("/"): path = join(self.env.app.srcdir, path) try: with open(path) as f: return f.read(), path except Exception as e: raise SphinxError( f"bokeh-plot:: error reading source for {self.env.docname!r}: {e!r}" ) def process_source(self, source, path, js_filename): Model._clear_extensions() root, docstring = _evaluate_source(source, path, self.env) height_hint = root._sphinx_height_hint() js_path = join(self.env.bokeh_plot_auxdir, js_filename) js, script_tag = autoload_static(root, RESOURCES, js_filename) with open(js_path, "w") as f: f.write(js) return (script_tag, js_path, source, docstring, height_hint)
class BokehPlotDirective(Directive): has_content = True optional_arguments = 2 option_spec = { "source-position": lambda x: choice(x, ("below", "above", "none")), "linenos": lambda x: True if flag(x) is None else False, } def run(self): env = self.state.document.settings.env # filename *or* python code content, but not both if self.arguments and self.content: raise SphinxError( "bokeh-plot:: directive can't have both args and content") # need docname not to look like a path docname = env.docname.replace("/", "-") if self.content: log.debug( f"[bokeh-plot] handling inline example in {env.docname!r}") path = env.bokeh_plot_auxdir # code runner just needs any real path source = "\n".join(self.content) else: try: log.debug( f"[bokeh-plot] handling external example in {env.docname!r}: {self.arguments[0]}" ) path = self.arguments[0] if not path.startswith("/"): path = join(env.app.srcdir, path) source = open(path).read() except Exception as e: raise SphinxError(f"{env.docname}: {e!r}") js_name = f"bokeh-plot-{uuid4().hex}-external-{docname}.js" try: (script, js, js_path, source) = _process_script(source, path, env, js_name) except Exception as e: raise RuntimeError( f"Sphinx bokeh-plot exception: \n\n{e}\n\n Failed on:\n\n {source}" ) env.bokeh_plot_files[js_name] = (script, js, js_path, source, dirname(env.docname)) # use the source file name to construct a friendly target_id target_id = f"{env.docname}.{basename(js_path)}" target = nodes.target("", "", ids=[target_id]) result = [target] linenos = self.options.get("linenos", False) code = nodes.literal_block(source, source, language="python", linenos=linenos, classes=[]) set_source_info(self, code) source_position = self.options.get("source-position", "below") if source_position == "above": result += [code] result += [nodes.raw("", script, format="html")] if source_position == "below": result += [code] return result
class BokehPlotDirective(BokehDirective): has_content = True optional_arguments = 2 option_spec = { "process-docstring": lambda x: flag(x) is None, "source-position": lambda x: choice(x, ("below", "above", "none")), "linenos": lambda x: flag(x) is None, } def run(self): source, path = self.process_args_or_content() dashed_docname = self.env.docname.replace("/", "-") js_filename = f"bokeh-content-{uuid4().hex}-{dashed_docname}.js" try: (script_tag, js_path, source, docstring, height_hint) = self.process_source(source, path, js_filename) except Exception as e: raise SphinxError(f"Error generating {js_filename}: \n\n{e}") self.env.bokeh_plot_files.add((js_path, dirname(self.env.docname))) # use the source file name to construct a friendly target_id target_id = f"{dashed_docname}.{basename(js_path)}" target = [nodes.target("", "", ids=[target_id])] self.process_sampledata(source) process_docstring = self.options.get("process-docstring", False) intro = self.parse( docstring, '<bokeh-content>') if docstring and process_docstring else [] above, below = self.process_code_block(source, docstring) autoload = [ autoload_script(height_hint=height_hint, script_tag=script_tag) ] return target + intro + above + autoload + below def process_code_block(self, source: str, docstring: str | None): source_position = self.options.get("source-position", "below") if source_position == "none": return [], [] source = _remove_module_docstring(source, docstring).strip() linenos = self.options.get("linenos", False) code_block = nodes.literal_block(source, source, language="python", linenos=linenos, classes=[]) set_source_info(self, code_block) if source_position == "above": return [code_block], [] if source_position == "below": return [], [code_block] def process_args_or_content(self): # filename *or* python code content, but not both if self.arguments and self.content: raise SphinxError( "bokeh-plot:: directive can't have both args and content") if self.content: log.debug( f"[bokeh-plot] handling inline content in {self.env.docname!r}" ) path = self.env.bokeh_plot_auxdir # code runner just needs any real path return "\n".join(self.content), path path = self.arguments[0] log.debug( f"[bokeh-plot] handling external content in {self.env.docname!r}: {path}" ) if not path.startswith("/"): path = join(self.env.app.srcdir, path) try: with open(path) as f: return f.read(), path except Exception as e: raise SphinxError( f"bokeh-plot:: error reading {path!r} for {self.env.docname!r}: {e!r}" ) def process_source(self, source, path, js_filename): Model._clear_extensions() root, docstring = _evaluate_source(source, path, self.env) height_hint = root._sphinx_height_hint() js_path = join(self.env.bokeh_plot_auxdir, js_filename) js, script_tag = autoload_static(root, RESOURCES, js_filename) with open(js_path, "w") as f: f.write(js) return (script_tag, js_path, source, docstring, height_hint) def process_sampledata(self, source): if not hasattr(self.env, 'solved_sampledata'): self.env.solved_sampledata = [] file, lineno = self.get_source_info() # collect links to all standalone examples if '/docs/examples/' in file and not file in self.env.solved_sampledata: self.env.solved_sampledata.append(file) if not hasattr(self.env, 'all_sampledata_xrefs'): self.env.all_sampledata_xrefs = [] if not hasattr(self.env, 'all_gallery_overview'): self.env.all_gallery_overview = [] self.env.all_gallery_overview.append({ 'docname': self.env.docname, }) regex = "(:|bokeh\.)sampledata(:|\.| import )\s*(\w+(\,\s*\w+)*)" matches = re.findall(regex, source) if matches: keywords = set() for m in matches: keywords.update(m[2].replace(" ", "").split(',')) for keyword in keywords: self.env.all_sampledata_xrefs.append({ 'docname': self.env.docname, 'keyword': keyword, })
def du_flag(node): " No argument allowed. " return directives.flag(du_astext(node))
def test_flag(self): # Raise error when there is an argument: self.assertEqual(None, directives.flag('')) self.assertRaises(ValueError, directives.flag, 'alles')