Source code for sphinx_terminhtml.directives.terminal

import ast
from pathlib import Path
from typing import List, Sequence, Optional, Type


from docutils.nodes import Node
from docutils.parsers.rst import directives
from docutils.statemachine import StringList
from sphinx.util.docutils import SphinxDirective
from docutils import nodes
from terminhtml.main import TerminHTML

from sphinx_terminhtml.cache import TerminalCache
from sphinx_terminhtml.logger import log
from sphinx_terminhtml.options import RunTerminalOptions, CWDRelativeTo


[docs]def html(content: str) -> nodes.raw: return nodes.raw(text=content, format="html")
def _get_list(input: str) -> List[str]: if not input.startswith("["): if input: return [input] return [] return ast.literal_eval(input) def _get_optional_path(in_path: Optional[str]) -> Optional[Path]: if in_path is None: return None return Path(in_path) def _validate_cwd_relative_to(argument: Optional[str]) -> Optional[CWDRelativeTo]: if argument is None: return None return directives.choice(argument, [val.value for val in CWDRelativeTo]) # type: ignore cache = TerminalCache()
[docs]class TerminHTMLDirective(SphinxDirective): required_arguments = 0 option_spec = { "setup": directives.unchanged, "input": _get_list, "prompt-matchers": _get_list, "allow-exceptions": directives.flag, "cwd": directives.unchanged, "cwd-relative-to": _validate_cwd_relative_to, "disable-cache": directives.flag, "echo": directives.flag, "no-force-color": directives.flag, } has_content = True always_setup_commands: List[str] = [] always_prompt_matchers: List[str] = []
[docs] def run(self) -> List[Node]: text = self._run_commands_in_temp_dir_generate_output_html() return [html(text)]
@property def commands(self) -> List[str]: return list(self.content) @property def root_source_dir(self) -> Path: return Path(self.env.srcdir) @property def current_file_folder(self) -> Path: file_str, line = self.get_source_info() return Path(file_str).parent @property def cwd(self) -> Optional[Path]: cwd = _get_optional_path(self.options.get("cwd")) if cwd is None: return None if cwd.is_absolute(): return cwd # Resolve path based on relative to option cwd_relative_to = self.options.get("cwd-relative-to", CWDRelativeTo.CWD) if cwd_relative_to == CWDRelativeTo.CWD: return cwd.resolve() if cwd_relative_to == CWDRelativeTo.SOURCES_ROOT: return (self.root_source_dir / cwd).resolve() if cwd_relative_to == CWDRelativeTo.CURRENT_SOURCE: return (self.current_file_folder / cwd).resolve() raise ValueError(f"Unknown cwd-relative-to: {cwd_relative_to}") def _run_commands_in_temp_dir_generate_output_html(self) -> str: self.options: RunTerminalOptions setup_command: str = self.options.get("setup", "") if setup_command: use_setup_commands = self.always_setup_commands + [setup_command] else: use_setup_commands = self.always_setup_commands prompt_matchers: List[str] = self.options.get("prompt-matchers", []) or [] if prompt_matchers: use_prompt_matchers = self.always_prompt_matchers + prompt_matchers else: use_prompt_matchers = self.always_prompt_matchers input: List[Optional[str]] = self.options.get("input", []) allow_exceptions: bool = "allow-exceptions" in self.options return self._load_cache_or_run_commands_in_temp_dir_get_output_list( use_setup_commands, input=input, allow_exceptions=allow_exceptions, prompt_matchers=use_prompt_matchers, cwd=self.cwd, ) def _load_cache_or_run_commands_in_temp_dir_get_output_list( self, setup_commands: List[str], input: List[Optional[str]], allow_exceptions: bool, prompt_matchers: List[str], cwd: Optional[Path] = None, ) -> str: self.content: StringList cache_enabled: bool = self.env.config.terminhtml_cache disable_this_terminal_cache: bool = "disable-cache" in self.options if cache_enabled and not disable_this_terminal_cache: cached_result = cache.get(list(self.content), self.options) if cached_result: return cached_result.content else: log.info(f"TerminHTML cache disabled, running commands {self.content}") result = self._run_commands_in_temp_dir_generate_html( setup_commands, input, allow_exceptions, prompt_matchers, cwd=cwd ) cache.set(self.commands, self.options, result) return result def _run_commands_in_temp_dir_generate_html( self, setup_commands: List[str], input: List[Optional[str]], allow_exceptions: bool, prompt_matchers: List[str], cwd: Optional[Path] = None, ) -> str: global_echo: bool = self.env.config.terminhtml_echo echo_this_terminal: bool = "echo" in self.options echo_now = global_echo or echo_this_terminal global_force_color: bool = self.env.config.terminhtml_force_color not_force_color_this_terminal: bool = "no-force-color" in self.options if not_force_color_this_terminal: force_color = False else: force_color = global_force_color terminhtml = TerminHTML.from_commands( self.commands, setup_commands, input=input, allow_exceptions=allow_exceptions, prompt_matchers=prompt_matchers, cwd=cwd, echo=echo_now, force_color=force_color, ) return terminhtml.to_html(full=False)
[docs]def create_terminhtml_directive_with_setup( setup_commands: Optional[Sequence[str]] = None, prompt_matchers: Optional[Sequence[str]] = None, ) -> Type[SphinxDirective]: use_setup_commands = list(setup_commands or []) use_prompt_matchers = list(prompt_matchers or []) class RunTerminalDirectiveWithSetup(TerminHTMLDirective): always_setup_commands = use_setup_commands always_prompt_matchers = use_prompt_matchers return RunTerminalDirectiveWithSetup