From 48c45c6a8c4501005766c2d821b5e9ddfbedd5fa Mon Sep 17 00:00:00 2001 From: Alvaro Leiva geisse Date: Fri, 30 Jun 2023 01:10:50 -0700 Subject: [PATCH] UPGRADE: myst-parser 1.0 (#479) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Chris Holdgraf Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Angus Hollands Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Chris Holdgraf Co-authored-by: Josh Mitchell Co-authored-by: Michael Aye Co-authored-by: Jan-Hendrik Müller <44469195+kolibril13@users.noreply.github.com> Co-authored-by: Oriol Abril-Pla Co-authored-by: mmcky Co-authored-by: mmcky --- .pre-commit-config.yaml | 2 +- CHANGELOG.md | 4 +- docs/authoring/custom-formats.Rmd | 2 +- docs/authoring/jupyter-notebooks.md | 16 ++-- docs/index.md | 2 +- myst_nb/__init__.py | 2 +- myst_nb/core/config.py | 32 +++++--- myst_nb/core/read.py | 25 +++--- myst_nb/core/render.py | 38 ++++----- myst_nb/docutils_.py | 36 ++++----- myst_nb/ext/glue/directives.py | 1 - myst_nb/sphinx_.py | 10 ++- myst_nb/warnings_.py | 103 ++++++++++++++++++++++++ pyproject.toml | 6 +- tests/nb_fixtures/reporter_warnings.txt | 9 +-- 15 files changed, 191 insertions(+), 97 deletions(-) create mode 100644 myst_nb/warnings_.py diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index a10c3699..a84a065e 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -48,7 +48,7 @@ repos: args: [--config-file=pyproject.toml] additional_dependencies: - importlib_metadata - - myst-parser~=0.18.0 + - myst-parser~=1.0.0 - "sphinx~=5.0" - nbclient - types-PyYAML diff --git a/CHANGELOG.md b/CHANGELOG.md index d0191248..0699e778 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -92,7 +92,7 @@ WARNING: 'jupyter_execute_notebooks' is deprecated for 'nb_execution_mode' [myst `nb_render_priority` has been removed and replaced by `nb_mime_priority_overrides`, which has a different format and is more flexible. See [Outputs MIME priority](docs/render/format_code_cells.md) for more information. -As per the changes in [`myst_parser`](myst:develop/_changelog), the `dollarmath` syntax extension is no longer included by default. +As per the changes in [`myst_parser`](inv:myst#develop/_changelog), the `dollarmath` syntax extension is no longer included by default. To re-add this extension, ensure that it is specified in your `conf.py`: `myst_enable_extensions = ["dollarmath"]`. For cell-level configuration the top-level key `render` has now been deprecated for `mystnb`. @@ -168,7 +168,7 @@ See [Embedding outputs as variables](docs/render/glue.md) for more details. - `nbconvert` - Updated: - `Python`: `3.6+ -> 3.7+` - - `myst_parser`: [`0.15 -> 0.17`](myst:develop/_changelog) + - `myst_parser`: [`0.15 -> 0.17`](inv:myst#develop/_changelog) - `jupyter-cache`: [`0.4 -> 0.5`](https://github.com/executablebooks/jupyter-cache/blob/master/CHANGELOG.md) - `sphinx-togglebutton`: [`0.1 -> 0.3`](https://sphinx-togglebutton.readthedocs.io/en/latest/changelog.html) diff --git a/docs/authoring/custom-formats.Rmd b/docs/authoring/custom-formats.Rmd index c682702c..7575d250 100644 --- a/docs/authoring/custom-formats.Rmd +++ b/docs/authoring/custom-formats.Rmd @@ -21,7 +21,7 @@ nb_custom_formats = { ``` - The string should be a Python function that will be loaded by `import mylibrary.converter_function` -- The function should take a file's contents (as a `str`) and return an [nbformat.NotebookNode](nbformat:api) +- The function should take a file's contents (as a `str`) and return an [nbformat.NotebookNode](inv:nbformat#api) If the function takes additional keyword arguments, then you can specify these as dictionary in a second argument. For example this is what the default conversion would look like: diff --git a/docs/authoring/jupyter-notebooks.md b/docs/authoring/jupyter-notebooks.md index 945ab336..58b1f71a 100644 --- a/docs/authoring/jupyter-notebooks.md +++ b/docs/authoring/jupyter-notebooks.md @@ -16,13 +16,13 @@ Sphinx using the MyST parser.[^download] :::{seealso} For more information about what you can write with MyST Markdown, see the -[MyST Parser documentation](myst:intro/get-started). +[MyST Parser documentation](inv:myst#intro/get-started). ::: ### Configuration -The MyST-NB parser derives from [the base MyST-Parser](myst:intro/get-started), and so all the same configuration options are available. -See the [MyST configuration options](myst:sphinx/config-options) for the full set of options, and [MyST syntax guide](myst:syntax/core) for all the syntax options. +The MyST-NB parser derives from [the base MyST-Parser](inv:myst#intro/get-started), and so all the same configuration options are available. +See the [MyST configuration options](inv:myst#sphinx/config-options) for the full set of options, and [MyST syntax guide](inv:myst#syntax/core) for all the syntax options. To build documentation from this notebook, the following options are set: @@ -38,7 +38,7 @@ myst_url_schemes = ("http", "https", "mailto") ``` :::{note} -Loading the `myst_nb` extension also activates the [`myst_parser`](myst:index) extension, for enabling the MyST flavour of Markdown. +Loading the `myst_nb` extension also activates the [`myst_parser`](inv:myst#index) extension, for enabling the MyST flavour of Markdown. It is not required to add this explicitly in the list of `extensions`. ::: @@ -53,7 +53,7 @@ For example, here's the MyST-NB logo: ![myst-nb logo](../_static/logo-wide.svg) -By adding `"html_image"` to the `myst_enable_extensions` list in the sphinx configuration ([see here](myst:syntax/images)), you can even add HTML `img` tags with attributes: +By adding `"html_image"` to the `myst_enable_extensions` list in the sphinx configuration ([see here](inv:myst#syntax/images)), you can even add HTML `img` tags with attributes: ```html logo @@ -66,7 +66,7 @@ For example, here's a note admonition block: :::::{note} **Wow**, a note! -It was generated with this code ([as explained here](myst:syntax/admonitions)): +It was generated with this code ([as explained here](inv:myst:std:label#syntax/admonitions)): ````md :::{note} @@ -77,7 +77,7 @@ It was generated with this code ([as explained here](myst:syntax/admonitions)): ::::: If you wish to use "bare" LaTeX equations, then you should add `"amsmath"` to the `myst_enable_extensions` list in the sphinx configuration. -This is [explained here](myst:syntax/amsmath), and works as such: +This is [explained here](inv:myst:std:label#syntax/amsmath), and works as such: ```latex \begin{equation} @@ -110,7 +110,7 @@ $$e^{i\pi} + 1 = 0$$ (euler) Euler's identity, equation {math:numref}`euler`, was elected one of the most beautiful mathematical formulas. -You can see the syntax used for this example [here in the MyST documentation](myst:syntax/math). +You can see the syntax used for this example [here in the MyST documentation](inv:myst:std:label#syntax/math). ## Code cells and outputs diff --git a/docs/index.md b/docs/index.md index 06fef4a1..d75b9260 100644 --- a/docs/index.md +++ b/docs/index.md @@ -86,7 +86,7 @@ Build single or collections of documents into multiple formats (HTML, PDF, ...). MyST-NB is a module within the [Executable Books Project](https://executablebooks.org), an international collaboration to build open source tools that facilitate publishing computational narratives using the Jupyter ecosystem. -It is also a core component of [Jupyter Book](jb:intro). +It is also a core component of [Jupyter Book](inv:jb#intro). Check out the [Gallery of Jupyter Books](https://executablebooks.org/en/latest/gallery), for inspiration from across the community. diff --git a/myst_nb/__init__.py b/myst_nb/__init__.py index 7fd25d88..8b156d3a 100644 --- a/myst_nb/__init__.py +++ b/myst_nb/__init__.py @@ -1,5 +1,5 @@ """A docutils/sphinx parser for Jupyter Notebooks.""" -__version__ = "0.17.2" +__version__ = "0.18.0" def setup(app): diff --git a/myst_nb/core/config.py b/myst_nb/core/config.py index 8f7b117f..3bf97e83 100644 --- a/myst_nb/core/config.py +++ b/myst_nb/core/config.py @@ -1,6 +1,7 @@ """Configuration for myst-nb.""" import dataclasses as dc from enum import Enum +import sys from typing import Any, Callable, Dict, Iterable, Optional, Sequence, Tuple from myst_parser.config.dc_validators import ( @@ -12,7 +13,13 @@ optional, validate_fields, ) -from typing_extensions import Literal + +if sys.version_info >= (3, 8): + from typing import Literal +else: + from typing_extensions import Literal # noqa: F401 + +from myst_nb.warnings_ import MystNBWarnings def custom_formats_converter(value: dict) -> Dict[str, Tuple[str, dict, bool]]: @@ -130,7 +137,7 @@ def __post_init__(self): default_factory=dict, metadata={ "help": "Custom formats for reading notebook; suffix -> reader", - "docutils_exclude": True, + "omit": ["docutils"], "sections": (Section.global_lvl, Section.read), }, ) @@ -184,7 +191,7 @@ def __post_init__(self): "validator": deep_mapping(instance_of(str), instance_of(str)), "help": "Mapping of kernel name regex to replacement kernel name" "(applied before execution)", - "docutils_exclude": True, + "omit": ["docutils"], "sections": (Section.global_lvl, Section.execute), }, ) @@ -228,7 +235,7 @@ def __post_init__(self): "validator": deep_iterable(instance_of(str)), "help": "Exclude (POSIX) glob patterns for notebooks", "legacy_name": "execution_excludepatterns", - "docutils_exclude": True, + "omit": ["docutils"], "sections": (Section.global_lvl, Section.execute), }, ) @@ -395,7 +402,7 @@ def __post_init__(self): "help": "Overrides for the base render priority of mime types: " "list of (builder name, mime type, priority)", # TODO how to allow this in docutils? - "docutils_exclude": True, + "omit": ["docutils"], "sections": (Section.global_lvl, Section.file_lvl, Section.render), }, repr=False, @@ -462,7 +469,7 @@ def __post_init__(self): metadata={ "validator": deep_mapping(instance_of(str), instance_of((str, int))), "help": "Options for image outputs (class|alt|height|width|scale|align)", - "docutils_exclude": True, + "omit": ["docutils"], # TODO backward-compatible change to "image_options"? "cell_key": "image", "sections": ( @@ -479,7 +486,7 @@ def __post_init__(self): metadata={ "validator": deep_mapping(instance_of(str), instance_of((str, int))), "help": "Options for figure outputs (classes|name|caption|caption_before)", - "docutils_exclude": True, + "omit": ["docutils"], "cell_key": "figure", "sections": ( Section.global_lvl, @@ -513,7 +520,7 @@ def __post_init__(self): instance_of(str), deep_mapping(instance_of(str), instance_of(str)) ), "help": "Javascript to be loaded on pages containing ipywidgets", - "docutils_exclude": True, + "omit": ["docutils"], "sections": (Section.global_lvl, Section.render), }, repr=False, @@ -575,7 +582,7 @@ def get_cell_level_config( self, field_name: str, cell_metadata: Dict[str, Any], - warning_callback: Callable[[str, str], Any], + warning_callback: Callable[[str, MystNBWarnings], Any], ) -> Any: """Get a configuration value at the cell level. @@ -601,7 +608,7 @@ def get_cell_level_config( warning_callback( f"Deprecated `cell_metadata_key` 'render' " f"found, replace with {self.cell_metadata_key!r}", - "cell_metadata_key", + MystNBWarnings.CELL_METADATA_KEY, ) cell_meta = cell_metadata["render"] else: @@ -619,7 +626,10 @@ def get_cell_level_config( field.metadata["validator"](self, field, value) return value except Exception as exc: - warning_callback(f"Cell metadata invalid: {exc}", "cell_config") + warning_callback( + f"Cell metadata invalid: {exc}", + MystNBWarnings.CELL_CONFIG, + ) # default/global/file level should have already been merged return getattr(self, field.name) diff --git a/myst_nb/core/read.py b/myst_nb/core/read.py index ad011049..2b3dae4b 100644 --- a/myst_nb/core/read.py +++ b/myst_nb/core/read.py @@ -312,25 +312,22 @@ class _MockDirective: def _read_fenced_cell(token, cell_index, cell_type): - from myst_parser.parsers.directives import ( - DirectiveParsingError, - parse_directive_text, - ) + from myst_parser.parsers.directives import parse_directive_text - try: - _, options, body_lines, _ = parse_directive_text( - directive_class=_MockDirective, - first_line="", - content=token.content, - validate_options=False, - ) - except DirectiveParsingError as err: + result = parse_directive_text( + directive_class=_MockDirective, + first_line="", + content=token.content, + validate_options=False, + ) + if result.warnings: raise MystMetadataParsingError( "{} cell {} at line {} could not be read: {}".format( - cell_type, cell_index, token.map[0] + 1, err + cell_type, cell_index, token.map[0] + 1, result.warnings[0] ) ) - return options, body_lines + + return result.options, result.body def _read_cell_metadata(token, cell_index): diff --git a/myst_nb/core/render.py b/myst_nb/core/render.py index e28b856b..54f1762d 100644 --- a/myst_nb/core/render.py +++ b/myst_nb/core/render.py @@ -28,8 +28,9 @@ from myst_nb.core.config import NbParserConfig from myst_nb.core.execute import NotebookClientBase -from myst_nb.core.loggers import DEFAULT_LOG_TYPE, LoggerType +from myst_nb.core.loggers import LoggerType # DEFAULT_LOG_TYPE, from myst_nb.core.utils import coalesce_streams +from myst_nb.warnings_ import MystNBWarnings, create_warning if TYPE_CHECKING: from markdown_it.tree import SyntaxTreeNode @@ -57,7 +58,6 @@ class MditRenderMixin: # required by mypy md_options: dict[str, Any] document: nodes.document - create_warning: Any render_children: Any add_line_and_source_path: Any add_line_and_source_path_r: Any @@ -95,8 +95,8 @@ def get_cell_level_config( :param cell_metadata: the metadata for the cell """ - def _callback(msg: str, subtype: str): - self.create_warning(msg, line=line, subtype=subtype) + def _callback(msg: str, subtype: MystNBWarnings): + create_warning(self.document, msg, line=line, subtype=subtype) return self.nb_config.get_cell_level_config(field, cell_metadata, _callback) @@ -222,10 +222,11 @@ def _get_nb_source_code_lexer( # TODO this will create a warning for every cell, but perhaps # it should only be a single warning for the notebook (as previously) # TODO allow user to set default lexer? - self.create_warning( + create_warning( + self.document, f"No source code lexer found for notebook cell {cell_index + 1}", - wtype=DEFAULT_LOG_TYPE, - subtype="lexer", + # wtype=DEFAULT_LOG_TYPE, + subtype=MystNBWarnings.LEXER, line=line, append_to=self.current_node, ) @@ -310,11 +311,6 @@ class MimeData: """Index of the output in the cell""" line: int | None = None """Source line of the cell""" - md_headings: bool = False - """Whether to render headings in text/markdown blocks.""" - # we can only do this if know the content will be rendered into the main body - # of the document, e.g. not inside a container node - # (otherwise it will break the structure of the AST) @property def string(self) -> str: @@ -598,9 +594,7 @@ def render_markdown(self, data: MimeData) -> list[nodes.Element]: fmt = self.renderer.get_cell_level_config( "render_markdown_format", data.cell_metadata, line=data.line ) - return self._render_markdown_base( - data, fmt=fmt, inline=False, allow_headings=data.md_headings - ) + return self._render_markdown_base(data, fmt=fmt, inline=False) def render_text_plain(self, data: MimeData) -> list[nodes.Element]: """Render a notebook text/plain mime data output.""" @@ -753,9 +747,7 @@ def render_markdown_inline(self, data: MimeData) -> list[nodes.Element]: fmt = self.renderer.get_cell_level_config( "render_markdown_format", data.cell_metadata, line=data.line ) - return self._render_markdown_base( - data, fmt=fmt, inline=True, allow_headings=data.md_headings - ) + return self._render_markdown_base(data, fmt=fmt, inline=True) def render_text_plain_inline(self, data: MimeData) -> list[nodes.Element]: """Render a notebook text/plain mime data output.""" @@ -796,7 +788,7 @@ def render_widget_view_inline(self, data: MimeData) -> list[nodes.Element]: return self.render_widget_view(data) def _render_markdown_base( - self, data: MimeData, *, fmt: str, inline: bool, allow_headings: bool + self, data: MimeData, *, fmt: str, inline: bool ) -> list[nodes.Element]: """Base render for a notebook markdown mime output (block or inline).""" psuedo_element = nodes.Element() # element to hold the parsed markdown @@ -832,7 +824,6 @@ def _render_markdown_base( data.string, data.line or 0, inline=inline, - allow_headings=allow_headings, ) finally: # restore the parser @@ -986,11 +977,12 @@ def create_figure_context( caption.source = self.document["source"] caption.line = line elif not (isinstance(first_node, nodes.comment) and len(first_node) == 0): - self.create_warning( + create_warning( + self.document, "Figure caption must be a paragraph or empty comment.", line=line, - wtype=DEFAULT_LOG_TYPE, - subtype="fig_caption", + # wtype=DEFAULT_LOG_TYPE, + subtype=MystNBWarnings.FIG_CAPTION, ) self.current_node.append(figure_node) diff --git a/myst_nb/docutils_.py b/myst_nb/docutils_.py index 9738a0f0..7da9c6db 100644 --- a/myst_nb/docutils_.py +++ b/myst_nb/docutils_.py @@ -14,14 +14,7 @@ from markdown_it.token import Token from markdown_it.tree import SyntaxTreeNode from myst_parser.config.main import MdParserConfig, merge_file_level -from myst_parser.mdit_to_docutils.base import ( - DocutilsRenderer, - create_warning, - token_line, -) -from myst_parser.parsers.docutils_ import ( - DOCUTILS_EXCLUDED_ARGS as DOCUTILS_EXCLUDED_ARGS_MYST, -) +from myst_parser.mdit_to_docutils.base import DocutilsRenderer, token_line from myst_parser.parsers.docutils_ import Parser as MystParser from myst_parser.parsers.docutils_ import create_myst_config, create_myst_settings_spec from myst_parser.parsers.mdit import create_md_parser @@ -32,7 +25,7 @@ from myst_nb import static from myst_nb.core.config import NbParserConfig from myst_nb.core.execute import create_client -from myst_nb.core.loggers import DEFAULT_LOG_TYPE, DocutilsDocLogger +from myst_nb.core.loggers import DocutilsDocLogger # DEFAULT_LOG_TYPE, from myst_nb.core.nb_to_tokens import nb_node_to_dict, notebook_to_tokens from myst_nb.core.read import ( NbReader, @@ -50,6 +43,7 @@ ) from myst_nb.ext.eval import load_eval_docutils from myst_nb.ext.glue import load_glue_docutils +from myst_nb.warnings_ import MystNBWarnings, create_warning DOCUTILS_EXCLUDED_ARGS = list( {f.name for f in NbParserConfig.get_fields() if f.metadata.get("docutils_exclude")} @@ -81,7 +75,7 @@ class Parser(MystParser): settings_spec = ( "MyST-NB options", None, - create_myst_settings_spec(DOCUTILS_EXCLUDED_ARGS, NbParserConfig, "nb_"), + create_myst_settings_spec(NbParserConfig, "nb_"), *MystParser.settings_spec, ) """Runtime settings specification.""" @@ -116,18 +110,14 @@ def _parse(self, inputstring: str, document: nodes.document) -> None: # get markdown parsing configuration try: - md_config = create_myst_config( - document.settings, DOCUTILS_EXCLUDED_ARGS_MYST - ) + md_config = create_myst_config(document.settings) except (TypeError, ValueError) as error: logger.error(f"myst configuration invalid: {error.args[0]}") md_config = MdParserConfig() # get notebook rendering configuration try: - nb_config = create_myst_config( - document.settings, DOCUTILS_EXCLUDED_ARGS, NbParserConfig, "nb_" - ) + nb_config = create_myst_config(document.settings, NbParserConfig, "nb_") except (TypeError, ValueError) as error: logger.error(f"myst-nb configuration invalid: {error.args[0]}") nb_config = NbParserConfig() @@ -310,13 +300,14 @@ def _render_nb_cell_code_outputs( mime_type = next(x for x in mime_priority if x in output["data"]) except StopIteration: if output["data"]: - self.create_warning( + create_warning( + self.document, "No output mime type found from render_priority " f"(cell<{cell_index}>.output<{output_index}>", line=line, append_to=self.current_node, - wtype=DEFAULT_LOG_TYPE, - subtype="mime_type", + # wtype=DEFAULT_LOG_TYPE, + subtype=MystNBWarnings.MIME_TYPE, ) else: figure_options = ( @@ -341,12 +332,13 @@ def _render_nb_cell_code_outputs( self.current_node.extend(_nodes) self.add_line_and_source_path_r(_nodes, token) else: - self.create_warning( + create_warning( + self.document, f"Unsupported output type: {output.output_type}", line=line, append_to=self.current_node, - wtype=DEFAULT_LOG_TYPE, - subtype="output_type", + # wtype=DEFAULT_LOG_TYPE, + subtype=MystNBWarnings.OUTPUT_TYPE, ) diff --git a/myst_nb/ext/glue/directives.py b/myst_nb/ext/glue/directives.py index fc63376c..4d42c15a 100644 --- a/myst_nb/ext/glue/directives.py +++ b/myst_nb/ext/glue/directives.py @@ -106,7 +106,6 @@ def run(self) -> List[nodes.Node]: }, output_metadata=result.metadata, line=self.line, - md_headings=True, ) _nodes = result.nb_renderer.render_markdown(mime) self.set_source_info(_nodes) diff --git a/myst_nb/sphinx_.py b/myst_nb/sphinx_.py index 62bd9a7c..dba36531 100644 --- a/myst_nb/sphinx_.py +++ b/myst_nb/sphinx_.py @@ -13,7 +13,7 @@ from markdown_it.tree import SyntaxTreeNode from myst_parser.config.main import MdParserConfig, merge_file_level from myst_parser.mdit_to_docutils.base import token_line -from myst_parser.mdit_to_docutils.sphinx_ import SphinxRenderer, create_warning +from myst_parser.mdit_to_docutils.sphinx_ import SphinxRenderer from myst_parser.parsers.mdit import create_md_parser from myst_parser.parsers.sphinx_ import MystParser import nbformat @@ -38,6 +38,7 @@ get_mime_priority, load_renderer, ) +from myst_nb.warnings_ import MystNBWarnings, create_warning SPHINX_LOGGER = sphinx_logging.getLogger(__name__) @@ -301,12 +302,13 @@ def _render_nb_cell_code_outputs( self.add_line_and_source_path_r([mime_bundle], token) self.current_node.append(mime_bundle) else: - self.create_warning( + create_warning( + self.document, f"Unsupported output type: {output.output_type}", line=line, append_to=self.current_node, - wtype=DEFAULT_LOG_TYPE, - subtype="output_type", + # wtype=DEFAULT_LOG_TYPE, + subtype=MystNBWarnings.OUTPUT_TYPE, ) diff --git a/myst_nb/warnings_.py b/myst_nb/warnings_.py new file mode 100644 index 00000000..de8e66ce --- /dev/null +++ b/myst_nb/warnings_.py @@ -0,0 +1,103 @@ +"""Central handling of warnings for the myst-nb extension.""" +from __future__ import annotations + +from enum import Enum +from typing import Sequence + +from docutils import nodes +from myst_parser.warnings_ import MystWarnings +from myst_parser.warnings_ import create_warning as myst_parser_create_warnings + +__all__ = [ + "MystWarnings", + "MystNBWarnings", + "create_warning", +] + + +class MystNBWarnings(Enum): + """MySTNB warning types.""" + + LEXER = "lexer" + """Issue resolving lexer""" + + FIG_CAPTION = "fig_caption" + """Issue resoliving figure caption""" + + MIME_TYPE = "mime_type" + """Issue resolving MIME type""" + OUTPUT_TYPE = "output_type" + """Issue resolving Output type""" + + CELL_METADATA_KEY = "cell_metadata_key" + """Issue with a key in a cell's `metadata` dictionary.""" + CELL_CONFIG = "cell_config" + """Issue with a cell's configuration or metadata.""" + + +def _is_suppressed_warning( + type: str, subtype: str, suppress_warnings: Sequence[str] +) -> bool: + """Check whether the warning is suppressed or not. + + Mirrors: + https://github.com/sphinx-doc/sphinx/blob/47d9035bca9e83d6db30a0726a02dc9265bd66b1/sphinx/util/logging.py + """ + if type is None: + return False + + subtarget: str | None + + for warning_type in suppress_warnings: + if "." in warning_type: + target, subtarget = warning_type.split(".", 1) + else: + target, subtarget = warning_type, None + + if target == type and subtarget in (None, subtype, "*"): + return True + + return False + + +def create_warning( + document: nodes.document, + message: str, + subtype: MystNBWarnings | MystWarnings, + *, + line: int | None = None, + append_to: nodes.Element | None = None, +) -> nodes.system_message | None: + """Generate a warning, logging if it is necessary. + + If the warning type is listed in the ``suppress_warnings`` configuration, + then ``None`` will be returned and no warning logged. + """ + # Pass off Myst Parser warnings to that package + if isinstance(subtype, MystWarnings): + myst_parser_create_warnings( + document=document, + message=message, + subtype=subtype, + line=line, + append_to=append_to, + ) + + wtype = "myst-nb" + # figure out whether to suppress the warning, if sphinx is available, + # it will have been set up by the Sphinx environment, + # otherwise we will use the configuration set by docutils + suppress_warnings: Sequence[str] = [] + try: + suppress_warnings = document.settings.env.app.config.suppress_warnings + except AttributeError: + suppress_warnings = document.settings.myst_suppress_warnings or [] + if _is_suppressed_warning(wtype, subtype.value, suppress_warnings): + return None + + kwargs = {"line": line} if line is not None else {} + message = f"{message} [{wtype}.{subtype.value}]" + msg_node = document.reporter.warning(message, **kwargs) + if append_to is not None: + append_to.append(msg_node) + return msg_node diff --git a/pyproject.toml b/pyproject.toml index f0cafba4..290d31c6 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -38,7 +38,7 @@ dependencies = [ "ipython", "jupyter-cache>=0.5,<0.7", "nbclient", # nbclient version pinned by jupyter-client - "myst-parser~=0.18.0", + "myst-parser~=1.0.0", "nbformat~=5.0", "pyyaml", "sphinx>=4,<6", @@ -71,7 +71,7 @@ rtd = [ "alabaster", "altair", "bokeh", - "coconut>=1.4.3,<3.1.0", + "coconut>=1.4.3,<2.3.0", "ipykernel~=5.5", "ipywidgets", "jupytext>=1.11.2,<1.15.0", @@ -79,7 +79,7 @@ rtd = [ "numpy", "pandas", "plotly", - "sphinx-book-theme>=0.3,<1.1", + "sphinx-book-theme~=1.0.0", "sphinx-copybutton", "sphinx-design~=0.4.0", "sphinxcontrib-bibtex", diff --git a/tests/nb_fixtures/reporter_warnings.txt b/tests/nb_fixtures/reporter_warnings.txt index 813d082c..1e226162 100644 --- a/tests/nb_fixtures/reporter_warnings.txt +++ b/tests/nb_fixtures/reporter_warnings.txt @@ -10,10 +10,9 @@ cells: source: | {unknown}`a` . -:20002: (ERROR/3) Unknown interpreted text role "unknown". +:20002: (WARNING/2) Unknown interpreted text role "unknown". [myst.role_unknown] . - Unknown directive: . cells: @@ -24,7 +23,7 @@ cells: ```{xyz} ``` . -:10003: (ERROR/3) Unknown directive type "xyz". +:10003: (WARNING/2) Unknown directive type: 'xyz' [myst.directive_unknown] . Directive parsing error: @@ -66,5 +65,5 @@ cells: [a]: c . -:20004: (WARNING/2) Duplicate reference definition: A [myst.ref] -. \ No newline at end of file +:20004: (WARNING/2) Duplicate reference definition: A [myst.duplicate_def] +.