From 18b0433fc2a1cde547c60007ea473716d64e721e Mon Sep 17 00:00:00 2001 From: spacemanspiff2007 <10754716+spacemanspiff2007@users.noreply.github.com> Date: Fri, 15 Nov 2024 14:47:20 +0100 Subject: [PATCH] ci --- doc/conf.py | 2 +- doc/description.rst | 23 ++++++++---- src/sphinx_exec_code/sphinx_spec.py | 56 ++++++++++++++++++----------- tests/test_sphinx_spec.py | 10 ++++-- 4 files changed, 62 insertions(+), 29 deletions(-) diff --git a/doc/conf.py b/doc/conf.py index 84afc91..c1b5e24 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -24,7 +24,7 @@ # -- Project information ----------------------------------------------------- project = 'sphinx-exec-code' -copyright = '2021, spacemanspiff2007' +copyright = '2024, spacemanspiff2007' author = 'spacemanspiff2007' # The full version, including alpha/beta/rc tags diff --git a/doc/description.rst b/doc/description.rst index c796d4a..e027429 100644 --- a/doc/description.rst +++ b/doc/description.rst @@ -28,8 +28,8 @@ for a detailed description hide/hide_output: Will hide the corresponding block -name - Define implicit target name that can be referenced by using ref. For example: +name/name_output + Define implicit target name that can be referenced by using ref caption/caption_output Will add a caption above the block linenos/linenos_output @@ -183,16 +183,18 @@ With the combination of ``skip`` and ``hide`` it's possible to "simulate" every Further Examples ------------------------------ -This is an example with captions, highlights and name +This is an example with captions, highlights and name. + .. code-block:: python .. exec_code:: :lineno-start: 5 - :emphasize-lines: 1, 3 + :emphasize-lines: 1, 4 :caption: This is an important caption :caption_output: This is an important output caption :name: my_example_1 + :name_output: my_output_1 print('My') # This is a comment @@ -205,10 +207,11 @@ Generated view .. exec_code:: :lineno-start: 5 - :emphasize-lines: 1, 3 + :emphasize-lines: 1, 4 :caption: This is an important caption :caption_output: This is an important output caption :name: my_example_1 + :name_output: my_output_1 print('My') # This is a comment @@ -217,4 +220,12 @@ Generated view ---- -See :ref:`this code snippet ` for an example. +Create a link using to the blocks by using the name: + +.. code-block:: text + + See :ref:`this code snippet ` for an example + See :ref:`this code snippet ` for an example output + +See :ref:`this code snippet ` for an example +See :ref:`this code snippet ` for an example output diff --git a/src/sphinx_exec_code/sphinx_spec.py b/src/sphinx_exec_code/sphinx_spec.py index 3c32fa2..58084f0 100644 --- a/src/sphinx_exec_code/sphinx_spec.py +++ b/src/sphinx_exec_code/sphinx_spec.py @@ -1,7 +1,7 @@ from typing import Any, ClassVar, Dict, Final, Tuple from docutils.nodes import literal_block -from docutils.parsers.rst import directives # type: ignore +from docutils.parsers.rst import directives from sphinx.directives.code import CodeBlock from sphinx.util.typing import OptionSpec @@ -10,14 +10,17 @@ class SphinxSpecBase: defaults: ClassVar[Dict[str, str]] - drop_code_block_option: ClassVar[Tuple[str, ...]] @staticmethod - def _alias_to_name(alias: str) -> str: + def alias_to_name(alias: str) -> str: raise NotImplementedError() @staticmethod - def _name_to_alias(name: str) -> str: + def name_to_alias(name: str) -> str: + raise NotImplementedError() + + @staticmethod + def post_process_spec(spec: Dict[str, Any], options: Dict[str, Any]) -> None: raise NotImplementedError() def __init__(self, spec: Dict[str, Any]) -> None: @@ -34,12 +37,13 @@ def set_block_spec(self, block: literal_block) -> None: def from_options(cls, options: Dict[str, Any]) -> 'SphinxSpecBase': spec_names = tuple(cls.create_spec().keys()) - spec = {cls._alias_to_name(n): v for n, v in cls.defaults.items()} + spec = {cls.alias_to_name(n): v for n, v in cls.defaults.items()} for name in spec_names: if name not in options: continue - spec[cls._alias_to_name(name)] = options[name] + spec[cls.alias_to_name(name)] = options[name] + cls.post_process_spec(spec, options) return cls(spec=spec) @classmethod @@ -48,17 +52,15 @@ def create_spec(cls) -> OptionSpec: # spec from CodeBlock this_spec: OptionSpec = {} for name, directive in CodeBlock.option_spec.items(): - if name in cls.drop_code_block_option: - continue - this_spec[cls._name_to_alias(name)] = directive + this_spec[cls.name_to_alias(name)] = directive # own flags after the default flags so we overwrite them in case we have duplicate names for name, default in cls.defaults.items(): # all own options are currently strings if isinstance(default, str): - this_spec[cls._name_to_alias(name)] = directives.unchanged + this_spec[cls.name_to_alias(name)] = directives.unchanged elif isinstance(default, bool): - this_spec[cls._name_to_alias(name)] = directives.flag + this_spec[cls.name_to_alias(name)] = directives.flag else: msg = f'Unsupported type {type(default)} for default "{name:s}"!' raise TypeError(msg) @@ -88,9 +90,7 @@ def get_specs(options: Dict[str, Any]) -> Tuple['SpecCode', 'SpecOutput']: class SpecCode(SphinxSpecBase): - drop_code_block_option: ClassVar = () defaults: ClassVar = { - 'caption': '', 'filename': '', 'hide_code': False, # deprecated 2024 - remove after some time, must come before the new hide flag! 'hide': False, @@ -98,16 +98,20 @@ class SpecCode(SphinxSpecBase): } @staticmethod - def _alias_to_name(alias: str) -> str: + def alias_to_name(alias: str) -> str: if alias == 'hide_code': log.info('The "hide_code" directive is deprecated! Use "hide" instead!') return 'hide' return alias @staticmethod - def _name_to_alias(name: str) -> str: + def name_to_alias(name: str) -> str: return name + @staticmethod + def post_process_spec(spec: Dict[str, Any], options :Dict[str, Any]) -> None: + return None + def __init__(self, **kwargs: Dict[str, Any]) -> None: super().__init__(**kwargs) self.filename: Final[str] = self.spec.pop('filename') @@ -115,20 +119,32 @@ def __init__(self, **kwargs: Dict[str, Any]) -> None: class SpecOutput(SphinxSpecBase): @staticmethod - def _alias_to_name(alias: str) -> str: + def alias_to_name(alias: str) -> str: if alias.endswith('_output'): return alias[:-7] return alias @staticmethod - def _name_to_alias(name: str) -> str: + def name_to_alias(name: str) -> str: if name.endswith('_output'): - return name[:-7] + return name return name + '_output' - drop_code_block_option: ClassVar = ('name', ) + @staticmethod + def post_process_spec(spec: Dict[str, Any], options: Dict[str, Any]) -> None: + # if we have a name for code but not for output we programmatically build it by appending the _output suffix + name_output = SpecOutput.name_to_alias('name') + if name_output in options: + return None + + name_code = SpecCode.name_to_alias('name') + if name_code not in options: + return None + + # if we have a name for input we create a name for output + spec['name'] = f'{options[name_code]:s}_output' + defaults: ClassVar = { - 'caption': '', 'hide': False, 'language': 'none', } diff --git a/tests/test_sphinx_spec.py b/tests/test_sphinx_spec.py index c7043f3..0a12deb 100644 --- a/tests/test_sphinx_spec.py +++ b/tests/test_sphinx_spec.py @@ -31,7 +31,7 @@ def test_invalid_options() -> None: 'Invalid option: hide-output! ' 'Supported: caption, caption_output, class, class_output, dedent, dedent_output, ' 'emphasize-lines, emphasize-lines_output, filename, force, force_output, hide, hide_code, hide_output, ' - 'language, language_output, lineno-start, lineno-start_output, linenos, linenos_output, name' + 'language, language_output, lineno-start, lineno-start_output, linenos, linenos_output, name, name_output' ) with pytest.raises(ValueError) as e: # noqa: PT011 @@ -41,5 +41,11 @@ def test_invalid_options() -> None: 'Invalid options: caption-output, hide-output! ' 'Supported: caption, caption_output, class, class_output, dedent, dedent_output, ' 'emphasize-lines, emphasize-lines_output, filename, force, force_output, hide, hide_code, hide_output, ' - 'language, language_output, lineno-start, lineno-start_output, linenos, linenos_output, name' + 'language, language_output, lineno-start, lineno-start_output, linenos, linenos_output, name, name_output' ) + + +def test_post_process() -> None: + assert SpecOutput.from_options({'name': 'asdf'}).spec == {'name': 'asdf_output'} + assert SpecOutput.from_options({'name_output': 'asdf'}).spec == {'name': 'asdf'} + assert SpecOutput.from_options({'name': '1', 'name_output': '2'}).spec == {'name': '2'}