Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

👌 IMPROVE: Add image options to glue:figure #403

Merged
merged 2 commits into from
Apr 21, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -61,10 +61,10 @@ jobs:
# this is why we run `coverage xml` afterwards (required by codecov)

- name: Upload to Codecov
if: matrix.os == 'ubuntu-latest' && matrix.python-version == 3.7 && matrix.sphinx == '>=3,<4' && github.repository == 'executablebooks/MyST-NB'
if: github.repository == 'executablebooks/MyST-NB'
uses: codecov/codecov-action@v1
with:
name: myst-nb-pytests-py3.7
name: myst-nb-pytests
flags: pytests
file: ./coverage.xml
fail_ci_if_error: true
Expand Down
26 changes: 24 additions & 2 deletions docs/use/glue.md
Original file line number Diff line number Diff line change
Expand Up @@ -240,10 +240,24 @@ My rounded mean: {glue:text}`boot_mean:.2f` (95% CI: {glue:text}`boot_clo:.2f`/{
### The `glue:figure` directive

With `glue:figure` you can apply more formatting to figure like objects,
such as giving them a caption and referencable label:
such as giving them a caption and referenceable label:

:::{table} `glue:figure` directive options
| Option | Type | Description |
| ------ | ---- | ----------- |
| alt | text | Alternate text of an image |
| height | length | The desired height of an image |
| width | length or percentage | The width of an image |
| scale | percentage | The uniform scaling factor of an image |
| class | text | A space-separated list of class names for the image |
| figwidth | length or percentage | The width of the figure |
| figclass | text | A space-separated list of class names for the figure |
| name | text | referenceable label for the figure |
:::

````md
```{glue:figure} boot_fig
:alt: "Alternative title"
:figwidth: 300px
:name: "fig-boot"

Expand All @@ -252,6 +266,7 @@ This is a **caption**, with an embedded `{glue:text}` element: {glue:text}`boot_
````

```{glue:figure} boot_fig
:alt: "Alternative title"
:figwidth: 300px
:name: "fig-boot"

Expand Down Expand Up @@ -291,7 +306,14 @@ A caption for a pandas table.
The `glue:math` directive, is specific to latex math outputs
(glued variables that contain a `text/latex` mimetype),
and works similarly to the [sphinx math directive](https://www.sphinx-doc.org/en/1.8/usage/restructuredtext/directives.html#math).
For example:

:::{table} `glue:math` directive options
| Option | Type | Description |
| ------ | ---- | ----------- |
| nowrap | flag | Prevent any wrapping of the given math in a math environment |
| class | text | A space-separated list of class names |
| label or name | text | referenceable label for the figure |
:::

```{code-cell} ipython3
import sympy as sym
Expand Down
12 changes: 8 additions & 4 deletions myst_nb/core/render.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
from nbformat import NotebookNode
from typing_extensions import Protocol

from myst_nb.core.config import NbParserConfig
from myst_nb.core.loggers import DEFAULT_LOG_TYPE, LoggerType

if TYPE_CHECKING:
Expand Down Expand Up @@ -94,6 +95,11 @@ def renderer(self) -> DocutilsNbRenderer | SphinxNbRenderer:
"""The renderer this output renderer is associated with."""
return self._renderer

@property
def config(self) -> NbParserConfig:
"""The notebook parser config"""
return self._renderer.nb_config

@property
def logger(self) -> LoggerType:
"""The logger for this renderer.
Expand Down Expand Up @@ -126,7 +132,7 @@ def write_file(

:returns: URI to use for referencing the file
"""
output_folder = self.renderer.nb_config.output_folder
output_folder = self.config.output_folder
filepath = Path(output_folder).joinpath(*path)
if not output_folder:
pass # do not output anything if output_folder is not set (docutils only)
Expand Down Expand Up @@ -183,9 +189,7 @@ def render_nb_metadata(self, metadata: dict) -> dict:
"body": sanitize_script_content(json.dumps(ipywidgets_mime)),
},
)
for i, (path, kwargs) in enumerate(
self.renderer.nb_config.ipywidgets_js.items()
):
for i, (path, kwargs) in enumerate(self.config.ipywidgets_js.items()):
self.add_js_file(f"ipywidgets_{i}", path, kwargs)

return metadata
Expand Down
30 changes: 25 additions & 5 deletions myst_nb/glue/directives.py
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ def run(self) -> List[nodes.Node]:
]

# TODO this "override" feels a bit hacky
cell_key = result.nb_renderer.renderer.nb_config.cell_render_key
cell_key = result.nb_renderer.config.cell_render_key
mime = MimeData(
"text/markdown",
result.data["text/markdown"],
Expand All @@ -134,7 +134,11 @@ def run(self) -> List[nodes.Node]:


class PasteFigureDirective(_PasteDirectiveBase):
"""A directive for pasting code outputs from notebooks, wrapped in a figure."""
"""A directive for pasting code outputs from notebooks, wrapped in a figure.

Mirrors:
https://github.com/docutils-mirror/docutils/blob/9649abee47b4ce4db51be1d90fcb1fb500fa78b3/docutils/parsers/rst/directives/images.py#95
"""

def align(argument):
return directives.choice(argument, ("left", "center", "right"))
Expand All @@ -143,6 +147,13 @@ def figwidth_value(argument):
return directives.length_or_percentage_or_unitless(argument, "px")

option_spec = {
# note we don't add converters for image options,
# since this is handled in `NbElementRenderer.render_image`
"alt": directives.unchanged,
"height": directives.unchanged,
"width": directives.unchanged,
"scale": directives.unchanged,
"class": directives.unchanged,
"figwidth": figwidth_value,
"figclass": directives.class_option,
"align": align,
Expand All @@ -155,7 +166,15 @@ def run(self):
data = retrieve_glue_data(self.document, self.arguments[0])
except RetrievalError as exc:
return [glue_warning(str(exc), self.document, self.line)]
paste_nodes = render_glue_output(data, self.document, self.line, self.source)
render: Dict[str, Any] = {}
for key in ("alt", "height", "width", "scale", "class"):
if key in self.options:
render.setdefault("image", {})[
key.replace("classes", "class")
] = self.options[key]
paste_nodes = render_glue_output(
data, self.document, self.line, self.source, render=render
)

# note: most of this is copied directly from sphinx.Figure

Expand Down Expand Up @@ -204,10 +223,11 @@ class PasteMathDirective(_PasteDirectiveBase):
"""A directive for pasting latex outputs from notebooks as math."""

option_spec = {
"label": directives.unchanged,
"name": directives.unchanged,
"class": directives.class_option,
"nowrap": directives.flag,
# these are equivalent
"label": directives.unchanged,
"name": directives.unchanged,
}

def run(self) -> List[nodes.Node]:
Expand Down
45 changes: 36 additions & 9 deletions myst_nb/glue/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,9 @@ def render_glue_output(
document: nodes.document,
line: int,
source: str,
inline=False,
*,
inline: bool = False,
render: Optional[Dict[str, Any]] = None,
) -> List[nodes.Node]:
"""Retrive the notebook output data for this glue key,
then return the docutils/sphinx nodes relevant to this data.
Expand All @@ -96,16 +98,32 @@ def render_glue_output(
:param line: The current source line number of the directive or role.
:param source: The current source path or description.
:param inline: Whether to render the output as inline (or block).
:param render: Cell-level render metadata

:returns: A tuple of (was the key found, the docutils/sphinx nodes).
"""
cell_metadata = {}
if render:
cell_metadata[data.nb_renderer.config.cell_render_key] = render
if is_sphinx(document):
_nodes = _render_output_sphinx(
data.nb_renderer, data.data, data.metadata, source, line, inline
data.nb_renderer,
data.data,
cell_metadata,
data.metadata,
source,
line,
inline,
)
else:
_nodes = _render_output_docutils(
data.nb_renderer, data.data, data.metadata, document, line, inline
data.nb_renderer,
data.data,
cell_metadata,
data.metadata,
document,
line,
inline,
)
# TODO rendering should perhaps return if it succeeded explicitly,
# and whether system_messages or not (required for roles)
Expand All @@ -115,15 +133,16 @@ def render_glue_output(
def _render_output_docutils(
nb_renderer: NbElementRenderer,
data: Dict[str, Any],
metadata: Dict[str, Any],
cell_metadata: Dict[str, Any],
output_metadata: Dict[str, Any],
document: nodes.document,
line: int,
inline=False,
) -> List[nodes.Node]:
"""Render the output in docutils (select mime priority directly)."""
mime_priority = get_mime_priority(
nb_renderer.renderer.nb_config.builder_name,
nb_renderer.renderer.nb_config.mime_priority_overrides,
nb_renderer.config.builder_name,
nb_renderer.config.mime_priority_overrides,
)
try:
mime_type = next(x for x in mime_priority if x in data)
Expand All @@ -139,7 +158,8 @@ def _render_output_docutils(
mime_data = MimeData(
mime_type,
data[mime_type],
output_metadata=metadata,
cell_metadata=cell_metadata,
output_metadata=output_metadata,
line=line,
)
if inline:
Expand All @@ -150,7 +170,8 @@ def _render_output_docutils(
def _render_output_sphinx(
nb_renderer: NbElementRenderer,
data: Dict[str, Any],
metadata: Dict[str, Any],
cell_metadata: Dict[str, Any],
output_metadata: Dict[str, Any],
source: str,
line: int,
inline=False,
Expand All @@ -161,7 +182,13 @@ def _render_output_sphinx(
for mime_type, content in data.items():
mime_container = nodes.container(mime_type=mime_type)
set_source_info(mime_container, source, line)
mime_data = MimeData(mime_type, content, output_metadata=metadata, line=line)
mime_data = MimeData(
mime_type,
content,
cell_metadata=cell_metadata,
output_metadata=output_metadata,
line=line,
)
if inline:
_nodes = nb_renderer.render_mime_type_inline(mime_data)
else:
Expand Down