mirror of
https://github.com/aykhans/AzSuicideDataVisualization.git
synced 2025-07-01 14:07:48 +00:00
first commit
This commit is contained in:
19
.venv/Lib/site-packages/nbconvert/preprocessors/__init__.py
Normal file
19
.venv/Lib/site-packages/nbconvert/preprocessors/__init__.py
Normal file
@ -0,0 +1,19 @@
|
||||
# Class base Preprocessors
|
||||
# Backwards compatability for imported name
|
||||
from nbclient.exceptions import CellExecutionError
|
||||
|
||||
from .base import Preprocessor
|
||||
from .clearmetadata import ClearMetadataPreprocessor
|
||||
from .clearoutput import ClearOutputPreprocessor
|
||||
|
||||
# decorated function Preprocessors
|
||||
from .coalescestreams import coalesce_streams
|
||||
from .convertfigures import ConvertFiguresPreprocessor
|
||||
from .csshtmlheader import CSSHTMLHeaderPreprocessor
|
||||
from .execute import ExecutePreprocessor
|
||||
from .extractoutput import ExtractOutputPreprocessor
|
||||
from .highlightmagics import HighlightMagicsPreprocessor
|
||||
from .latex import LatexPreprocessor
|
||||
from .regexremove import RegexRemovePreprocessor
|
||||
from .svg2pdf import SVG2PDFPreprocessor
|
||||
from .tagremove import TagRemovePreprocessor
|
89
.venv/Lib/site-packages/nbconvert/preprocessors/base.py
Normal file
89
.venv/Lib/site-packages/nbconvert/preprocessors/base.py
Normal file
@ -0,0 +1,89 @@
|
||||
"""Base class for preprocessors"""
|
||||
|
||||
# Copyright (c) IPython Development Team.
|
||||
# Distributed under the terms of the Modified BSD License.
|
||||
|
||||
from traitlets import Bool
|
||||
|
||||
from ..utils.base import NbConvertBase
|
||||
|
||||
|
||||
class Preprocessor(NbConvertBase):
|
||||
"""A configurable preprocessor
|
||||
|
||||
Inherit from this class if you wish to have configurability for your
|
||||
preprocessor.
|
||||
|
||||
Any configurable traitlets this class exposed will be configurable in
|
||||
profiles using c.SubClassName.attribute = value
|
||||
|
||||
You can overwrite `preprocess_cell()` to apply a transformation
|
||||
independently on each cell or `preprocess()` if you prefer your own
|
||||
logic. See corresponding docstring for information.
|
||||
|
||||
Disabled by default and can be enabled via the config by
|
||||
'c.YourPreprocessorName.enabled = True'
|
||||
"""
|
||||
|
||||
enabled = Bool(False).tag(config=True)
|
||||
|
||||
def __init__(self, **kw):
|
||||
"""
|
||||
Public constructor
|
||||
|
||||
Parameters
|
||||
----------
|
||||
config : Config
|
||||
Configuration file structure
|
||||
`**kw`
|
||||
Additional keyword arguments passed to parent
|
||||
"""
|
||||
|
||||
super().__init__(**kw)
|
||||
|
||||
def __call__(self, nb, resources):
|
||||
if self.enabled:
|
||||
self.log.debug("Applying preprocessor: %s", self.__class__.__name__)
|
||||
return self.preprocess(nb, resources)
|
||||
else:
|
||||
return nb, resources
|
||||
|
||||
def preprocess(self, nb, resources):
|
||||
"""
|
||||
Preprocessing to apply on each notebook.
|
||||
|
||||
Must return modified nb, resources.
|
||||
|
||||
If you wish to apply your preprocessing to each cell, you might want
|
||||
to override preprocess_cell method instead.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
nb : NotebookNode
|
||||
Notebook being converted
|
||||
resources : dictionary
|
||||
Additional resources used in the conversion process. Allows
|
||||
preprocessors to pass variables into the Jinja engine.
|
||||
"""
|
||||
for index, cell in enumerate(nb.cells):
|
||||
nb.cells[index], resources = self.preprocess_cell(cell, resources, index)
|
||||
return nb, resources
|
||||
|
||||
def preprocess_cell(self, cell, resources, index):
|
||||
"""
|
||||
Override if you want to apply some preprocessing to each cell.
|
||||
Must return modified cell and resource dictionary.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
cell : NotebookNode cell
|
||||
Notebook cell being processed
|
||||
resources : dictionary
|
||||
Additional resources used in the conversion process. Allows
|
||||
preprocessors to pass variables into the Jinja engine.
|
||||
index : int
|
||||
Index of the cell being processed
|
||||
"""
|
||||
|
||||
raise NotImplementedError("should be implemented by subclass")
|
||||
return cell, resources
|
104
.venv/Lib/site-packages/nbconvert/preprocessors/clearmetadata.py
Normal file
104
.venv/Lib/site-packages/nbconvert/preprocessors/clearmetadata.py
Normal file
@ -0,0 +1,104 @@
|
||||
"""Module containing a preprocessor that removes metadata from code cells"""
|
||||
|
||||
# Copyright (c) IPython Development Team.
|
||||
# Distributed under the terms of the Modified BSD License.
|
||||
|
||||
from traitlets import Bool, Set
|
||||
|
||||
from .base import Preprocessor
|
||||
|
||||
|
||||
class ClearMetadataPreprocessor(Preprocessor):
|
||||
"""
|
||||
Removes all the metadata from all code cells in a notebook.
|
||||
"""
|
||||
|
||||
clear_cell_metadata = Bool(
|
||||
True,
|
||||
help=("Flag to choose if cell metadata is to be cleared in addition to notebook metadata."),
|
||||
).tag(config=True)
|
||||
clear_notebook_metadata = Bool(
|
||||
True,
|
||||
help=("Flag to choose if notebook metadata is to be cleared in addition to cell metadata."),
|
||||
).tag(config=True)
|
||||
preserve_nb_metadata_mask = Set(
|
||||
[("language_info", "name")],
|
||||
help=(
|
||||
"Indicates the key paths to preserve when deleting metadata "
|
||||
"across both cells and notebook metadata fields. Tuples of "
|
||||
"keys can be passed to preserved specific nested values"
|
||||
),
|
||||
).tag(config=True)
|
||||
preserve_cell_metadata_mask = Set(
|
||||
help=(
|
||||
"Indicates the key paths to preserve when deleting metadata "
|
||||
"across both cells and notebook metadata fields. Tuples of "
|
||||
"keys can be passed to preserved specific nested values"
|
||||
)
|
||||
).tag(config=True)
|
||||
|
||||
def current_key(self, mask_key):
|
||||
if isinstance(mask_key, str):
|
||||
return mask_key
|
||||
elif len(mask_key) == 0:
|
||||
# Safeguard
|
||||
return None
|
||||
else:
|
||||
return mask_key[0]
|
||||
|
||||
def current_mask(self, mask):
|
||||
return {self.current_key(k) for k in mask if self.current_key(k) is not None}
|
||||
|
||||
def nested_masks(self, mask):
|
||||
return {
|
||||
self.current_key(k[0]): k[1:]
|
||||
for k in mask
|
||||
if k and not isinstance(k, str) and len(k) > 1
|
||||
}
|
||||
|
||||
def nested_filter(self, items, mask):
|
||||
keep_current = self.current_mask(mask)
|
||||
keep_nested_lookup = self.nested_masks(mask)
|
||||
for k, v in items:
|
||||
keep_nested = keep_nested_lookup.get(k)
|
||||
if k in keep_current:
|
||||
if keep_nested is not None:
|
||||
if isinstance(v, dict):
|
||||
yield k, dict(self.nested_filter(v.items(), keep_nested))
|
||||
else:
|
||||
yield k, v
|
||||
|
||||
def preprocess_cell(self, cell, resources, cell_index):
|
||||
"""
|
||||
All the code cells are returned with an empty metadata field.
|
||||
"""
|
||||
if self.clear_cell_metadata:
|
||||
if cell.cell_type == "code":
|
||||
# Remove metadata
|
||||
if "metadata" in cell:
|
||||
cell.metadata = dict(
|
||||
self.nested_filter(cell.metadata.items(), self.preserve_cell_metadata_mask)
|
||||
)
|
||||
return cell, resources
|
||||
|
||||
def preprocess(self, nb, resources):
|
||||
"""
|
||||
Preprocessing to apply on each notebook.
|
||||
|
||||
Must return modified nb, resources.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
nb : NotebookNode
|
||||
Notebook being converted
|
||||
resources : dictionary
|
||||
Additional resources used in the conversion process. Allows
|
||||
preprocessors to pass variables into the Jinja engine.
|
||||
"""
|
||||
nb, resources = super().preprocess(nb, resources)
|
||||
if self.clear_notebook_metadata:
|
||||
if "metadata" in nb:
|
||||
nb.metadata = dict(
|
||||
self.nested_filter(nb.metadata.items(), self.preserve_nb_metadata_mask)
|
||||
)
|
||||
return nb, resources
|
@ -0,0 +1,29 @@
|
||||
"""Module containing a preprocessor that removes the outputs from code cells"""
|
||||
|
||||
# Copyright (c) IPython Development Team.
|
||||
# Distributed under the terms of the Modified BSD License.
|
||||
|
||||
from traitlets import Set
|
||||
|
||||
from .base import Preprocessor
|
||||
|
||||
|
||||
class ClearOutputPreprocessor(Preprocessor):
|
||||
"""
|
||||
Removes the output from all code cells in a notebook.
|
||||
"""
|
||||
|
||||
remove_metadata_fields = Set({"collapsed", "scrolled"}).tag(config=True)
|
||||
|
||||
def preprocess_cell(self, cell, resources, cell_index):
|
||||
"""
|
||||
Apply a transformation on each cell. See base.py for details.
|
||||
"""
|
||||
if cell.cell_type == "code":
|
||||
cell.outputs = []
|
||||
cell.execution_count = None
|
||||
# Remove metadata associated with output
|
||||
if "metadata" in cell:
|
||||
for field in self.remove_metadata_fields:
|
||||
cell.metadata.pop(field, None)
|
||||
return cell, resources
|
@ -0,0 +1,81 @@
|
||||
"""Preprocessor for merging consecutive stream outputs for easier handling."""
|
||||
|
||||
# Copyright (c) IPython Development Team.
|
||||
# Distributed under the terms of the Modified BSD License.
|
||||
|
||||
import functools
|
||||
import re
|
||||
|
||||
from traitlets.log import get_logger
|
||||
|
||||
|
||||
def cell_preprocessor(function):
|
||||
"""
|
||||
Wrap a function to be executed on all cells of a notebook
|
||||
|
||||
The wrapped function should have these parameters:
|
||||
|
||||
cell : NotebookNode cell
|
||||
Notebook cell being processed
|
||||
resources : dictionary
|
||||
Additional resources used in the conversion process. Allows
|
||||
preprocessors to pass variables into the Jinja engine.
|
||||
index : int
|
||||
Index of the cell being processed
|
||||
"""
|
||||
|
||||
@functools.wraps(function)
|
||||
def wrappedfunc(nb, resources):
|
||||
get_logger().debug("Applying preprocessor: %s", function.__name__)
|
||||
for index, cell in enumerate(nb.cells):
|
||||
nb.cells[index], resources = function(cell, resources, index)
|
||||
return nb, resources
|
||||
|
||||
return wrappedfunc
|
||||
|
||||
|
||||
cr_pat = re.compile(r".*\r(?=[^\n])")
|
||||
|
||||
|
||||
@cell_preprocessor
|
||||
def coalesce_streams(cell, resources, index):
|
||||
"""
|
||||
Merge consecutive sequences of stream output into single stream
|
||||
to prevent extra newlines inserted at flush calls
|
||||
|
||||
Parameters
|
||||
----------
|
||||
cell : NotebookNode cell
|
||||
Notebook cell being processed
|
||||
resources : dictionary
|
||||
Additional resources used in the conversion process. Allows
|
||||
transformers to pass variables into the Jinja engine.
|
||||
index : int
|
||||
Index of the cell being processed
|
||||
"""
|
||||
|
||||
outputs = cell.get("outputs", [])
|
||||
if not outputs:
|
||||
return cell, resources
|
||||
|
||||
last = outputs[0]
|
||||
new_outputs = [last]
|
||||
for output in outputs[1:]:
|
||||
if (
|
||||
output.output_type == "stream"
|
||||
and last.output_type == "stream"
|
||||
and last.name == output.name
|
||||
):
|
||||
last.text += output.text
|
||||
|
||||
else:
|
||||
new_outputs.append(output)
|
||||
last = output
|
||||
|
||||
# process \r characters
|
||||
for output in new_outputs:
|
||||
if output.output_type == "stream" and "\r" in output.text:
|
||||
output.text = cr_pat.sub("", output.text)
|
||||
|
||||
cell.outputs = new_outputs
|
||||
return cell, resources
|
@ -0,0 +1,50 @@
|
||||
"""Module containing a preprocessor that converts outputs in the notebook from
|
||||
one format to another.
|
||||
"""
|
||||
|
||||
# Copyright (c) Jupyter Development Team.
|
||||
# Distributed under the terms of the Modified BSD License.
|
||||
|
||||
|
||||
from traitlets import Unicode
|
||||
|
||||
from .base import Preprocessor
|
||||
|
||||
|
||||
class ConvertFiguresPreprocessor(Preprocessor):
|
||||
"""
|
||||
Converts all of the outputs in a notebook from one format to another.
|
||||
"""
|
||||
|
||||
from_format = Unicode(help="Format the converter accepts").tag(config=True)
|
||||
to_format = Unicode(help="Format the converter writes").tag(config=True)
|
||||
|
||||
def __init__(self, **kw):
|
||||
"""
|
||||
Public constructor
|
||||
"""
|
||||
super().__init__(**kw)
|
||||
|
||||
def convert_figure(self, data_format, data):
|
||||
raise NotImplementedError()
|
||||
|
||||
def preprocess_cell(self, cell, resources, cell_index):
|
||||
"""
|
||||
Apply a transformation on each cell,
|
||||
|
||||
See base.py
|
||||
"""
|
||||
|
||||
# Loop through all of the datatypes of the outputs in the cell.
|
||||
for output in cell.get("outputs", []):
|
||||
if (
|
||||
output.output_type in {"execute_result", "display_data"}
|
||||
and self.from_format in output.data
|
||||
and self.to_format not in output.data
|
||||
):
|
||||
|
||||
output.data[self.to_format] = self.convert_figure(
|
||||
self.from_format, output.data[self.from_format]
|
||||
)
|
||||
|
||||
return cell, resources
|
@ -0,0 +1,92 @@
|
||||
"""Module that pre-processes the notebook for export to HTML.
|
||||
"""
|
||||
|
||||
# Copyright (c) Jupyter Development Team.
|
||||
# Distributed under the terms of the Modified BSD License.
|
||||
|
||||
import hashlib
|
||||
import os
|
||||
|
||||
from jupyterlab_pygments import JupyterStyle
|
||||
from pygments.style import Style
|
||||
from traitlets import Type, Unicode, Union
|
||||
|
||||
from .base import Preprocessor
|
||||
|
||||
try:
|
||||
from notebook import DEFAULT_STATIC_FILES_PATH
|
||||
except ImportError:
|
||||
DEFAULT_STATIC_FILES_PATH = None
|
||||
|
||||
|
||||
class CSSHTMLHeaderPreprocessor(Preprocessor):
|
||||
"""
|
||||
Preprocessor used to pre-process notebook for HTML output. Adds IPython notebook
|
||||
front-end CSS and Pygments CSS to HTML output.
|
||||
"""
|
||||
|
||||
highlight_class = Unicode(".highlight", help="CSS highlight class identifier").tag(config=True)
|
||||
|
||||
style = Union(
|
||||
[Unicode("default"), Type(klass=Style)],
|
||||
help="Name of the pygments style to use",
|
||||
default_value=JupyterStyle,
|
||||
).tag(config=True)
|
||||
|
||||
def __init__(self, *pargs, **kwargs):
|
||||
Preprocessor.__init__(self, *pargs, **kwargs)
|
||||
self._default_css_hash = None
|
||||
|
||||
def preprocess(self, nb, resources):
|
||||
"""Fetch and add CSS to the resource dictionary
|
||||
|
||||
Fetch CSS from IPython and Pygments to add at the beginning
|
||||
of the html files. Add this css in resources in the
|
||||
"inlining.css" key
|
||||
|
||||
Parameters
|
||||
----------
|
||||
nb : NotebookNode
|
||||
Notebook being converted
|
||||
resources : dictionary
|
||||
Additional resources used in the conversion process. Allows
|
||||
preprocessors to pass variables into the Jinja engine.
|
||||
"""
|
||||
resources["inlining"] = {}
|
||||
resources["inlining"]["css"] = self._generate_header(resources)
|
||||
return nb, resources
|
||||
|
||||
def _generate_header(self, resources):
|
||||
"""
|
||||
Fills self.header with lines of CSS extracted from IPython
|
||||
and Pygments.
|
||||
"""
|
||||
from pygments.formatters import HtmlFormatter
|
||||
|
||||
header = []
|
||||
|
||||
formatter = HtmlFormatter(style=self.style)
|
||||
pygments_css = formatter.get_style_defs(self.highlight_class)
|
||||
header.append(pygments_css)
|
||||
|
||||
# Load the user's custom CSS and IPython's default custom CSS. If they
|
||||
# differ, assume the user has made modifications to his/her custom CSS
|
||||
# and that we should inline it in the nbconvert output.
|
||||
config_dir = resources["config_dir"]
|
||||
custom_css_filename = os.path.join(config_dir, "custom", "custom.css")
|
||||
if os.path.isfile(custom_css_filename):
|
||||
if DEFAULT_STATIC_FILES_PATH and self._default_css_hash is None:
|
||||
self._default_css_hash = self._hash(
|
||||
os.path.join(DEFAULT_STATIC_FILES_PATH, "custom", "custom.css")
|
||||
)
|
||||
if self._hash(custom_css_filename) != self._default_css_hash:
|
||||
with open(custom_css_filename, encoding="utf-8") as f:
|
||||
header.append(f.read())
|
||||
return header
|
||||
|
||||
def _hash(self, filename):
|
||||
"""Compute the hash of a file."""
|
||||
md5 = hashlib.md5()
|
||||
with open(filename, "rb") as f:
|
||||
md5.update(f.read())
|
||||
return md5.digest()
|
111
.venv/Lib/site-packages/nbconvert/preprocessors/execute.py
Normal file
111
.venv/Lib/site-packages/nbconvert/preprocessors/execute.py
Normal file
@ -0,0 +1,111 @@
|
||||
"""Module containing a preprocessor that executes the code cells
|
||||
and updates outputs"""
|
||||
|
||||
from nbclient import NotebookClient
|
||||
from nbclient import execute as _execute
|
||||
|
||||
# Backwards compatability for imported name
|
||||
from nbclient.exceptions import CellExecutionError # noqa
|
||||
|
||||
# Copyright (c) IPython Development Team.
|
||||
# Distributed under the terms of the Modified BSD License.
|
||||
from nbformat import NotebookNode
|
||||
|
||||
from .base import Preprocessor
|
||||
|
||||
|
||||
def executenb(*args, **kwargs):
|
||||
from warnings import warn
|
||||
|
||||
warn(
|
||||
"The 'nbconvert.preprocessors.execute.executenb' function was moved to nbclient.execute. "
|
||||
"We recommend importing that library directly.",
|
||||
FutureWarning,
|
||||
)
|
||||
return _execute(*args, **kwargs)
|
||||
|
||||
|
||||
# We inherit from both classes to allow for traitlets to resolve as they did pre-6.0.
|
||||
# This unfortunately makes for some ugliness around initialization as NotebookClient
|
||||
# assumes it's a constructed class with a nb object that we have to hack around.
|
||||
class ExecutePreprocessor(Preprocessor, NotebookClient):
|
||||
"""
|
||||
Executes all the cells in a notebook
|
||||
"""
|
||||
|
||||
def __init__(self, **kw):
|
||||
nb = kw.get("nb")
|
||||
Preprocessor.__init__(self, nb=nb, **kw)
|
||||
NotebookClient.__init__(self, nb, **kw)
|
||||
|
||||
def _check_assign_resources(self, resources):
|
||||
if resources or not hasattr(self, "resources"):
|
||||
self.resources = resources
|
||||
|
||||
def preprocess(self, nb: NotebookNode, resources=None, km=None):
|
||||
"""
|
||||
Preprocess notebook executing each code cell.
|
||||
|
||||
The input argument *nb* is modified in-place.
|
||||
|
||||
Note that this function recalls NotebookClient.__init__, which may look wrong.
|
||||
However since the preprocess call acts line an init on execution state it's expected.
|
||||
Therefore, we need to capture it here again to properly reset because traitlet
|
||||
assignments are not passed. There is a risk if traitlets apply any side effects for
|
||||
dual init.
|
||||
The risk should be manageable, and this approach minimizes side-effects relative
|
||||
to other alternatives.
|
||||
|
||||
One alternative but rejected implementation would be to copy the client's init internals
|
||||
which has already gotten out of sync with nbclient 0.5 release before nbconvert 6.0 released.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
nb : NotebookNode
|
||||
Notebook being executed.
|
||||
resources : dictionary (optional)
|
||||
Additional resources used in the conversion process. For example,
|
||||
passing ``{'metadata': {'path': run_path}}`` sets the
|
||||
execution path to ``run_path``.
|
||||
km: KernelManager (optional)
|
||||
Optional kernel manager. If none is provided, a kernel manager will
|
||||
be created.
|
||||
|
||||
Returns
|
||||
-------
|
||||
nb : NotebookNode
|
||||
The executed notebook.
|
||||
resources : dictionary
|
||||
Additional resources used in the conversion process.
|
||||
"""
|
||||
NotebookClient.__init__(self, nb, km)
|
||||
self.reset_execution_trackers()
|
||||
self._check_assign_resources(resources)
|
||||
|
||||
with self.setup_kernel():
|
||||
info_msg = self.wait_for_reply(self.kc.kernel_info())
|
||||
self.nb.metadata["language_info"] = info_msg["content"]["language_info"]
|
||||
for index, cell in enumerate(self.nb.cells):
|
||||
self.preprocess_cell(cell, resources, index)
|
||||
self.set_widgets_metadata()
|
||||
|
||||
return self.nb, self.resources
|
||||
|
||||
def preprocess_cell(self, cell, resources, index):
|
||||
"""
|
||||
Override if you want to apply some preprocessing to each cell.
|
||||
Must return modified cell and resource dictionary.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
cell : NotebookNode cell
|
||||
Notebook cell being processed
|
||||
resources : dictionary
|
||||
Additional resources used in the conversion process. Allows
|
||||
preprocessors to pass variables into the Jinja engine.
|
||||
index : int
|
||||
Index of the cell being processed
|
||||
"""
|
||||
self._check_assign_resources(resources)
|
||||
cell = self.execute_cell(cell, index, store_history=True)
|
||||
return cell, self.resources
|
148
.venv/Lib/site-packages/nbconvert/preprocessors/extractoutput.py
Normal file
148
.venv/Lib/site-packages/nbconvert/preprocessors/extractoutput.py
Normal file
@ -0,0 +1,148 @@
|
||||
"""A preprocessor that extracts all of the outputs from the
|
||||
notebook file. The extracted outputs are returned in the 'resources' dictionary.
|
||||
"""
|
||||
|
||||
# Copyright (c) IPython Development Team.
|
||||
# Distributed under the terms of the Modified BSD License.
|
||||
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
from binascii import a2b_base64
|
||||
from mimetypes import guess_extension
|
||||
from textwrap import dedent
|
||||
|
||||
from traitlets import Set, Unicode
|
||||
|
||||
from .base import Preprocessor
|
||||
|
||||
|
||||
def guess_extension_without_jpe(mimetype):
|
||||
"""
|
||||
This function fixes a problem with '.jpe' extensions
|
||||
of jpeg images which are then not recognised by latex.
|
||||
For any other case, the function works in the same way
|
||||
as mimetypes.guess_extension
|
||||
"""
|
||||
ext = guess_extension(mimetype)
|
||||
if ext == ".jpe":
|
||||
ext = ".jpeg"
|
||||
return ext
|
||||
|
||||
|
||||
def platform_utf_8_encode(data):
|
||||
if isinstance(data, str):
|
||||
if sys.platform == "win32":
|
||||
data = data.replace("\n", "\r\n")
|
||||
data = data.encode("utf-8")
|
||||
return data
|
||||
|
||||
|
||||
class ExtractOutputPreprocessor(Preprocessor):
|
||||
"""
|
||||
Extracts all of the outputs from the notebook file. The extracted
|
||||
outputs are returned in the 'resources' dictionary.
|
||||
"""
|
||||
|
||||
output_filename_template = Unicode("{unique_key}_{cell_index}_{index}{extension}").tag(
|
||||
config=True
|
||||
)
|
||||
|
||||
extract_output_types = Set({"image/png", "image/jpeg", "image/svg+xml", "application/pdf"}).tag(
|
||||
config=True
|
||||
)
|
||||
|
||||
def preprocess_cell(self, cell, resources, cell_index):
|
||||
"""
|
||||
Apply a transformation on each cell,
|
||||
|
||||
Parameters
|
||||
----------
|
||||
cell : NotebookNode cell
|
||||
Notebook cell being processed
|
||||
resources : dictionary
|
||||
Additional resources used in the conversion process. Allows
|
||||
preprocessors to pass variables into the Jinja engine.
|
||||
cell_index : int
|
||||
Index of the cell being processed (see base.py)
|
||||
"""
|
||||
|
||||
# Get the unique key from the resource dict if it exists. If it does not
|
||||
# exist, use 'output' as the default. Also, get files directory if it
|
||||
# has been specified
|
||||
unique_key = resources.get("unique_key", "output")
|
||||
output_files_dir = resources.get("output_files_dir", None)
|
||||
|
||||
# Make sure outputs key exists
|
||||
if not isinstance(resources["outputs"], dict):
|
||||
resources["outputs"] = {}
|
||||
|
||||
# Loop through all of the outputs in the cell
|
||||
for index, out in enumerate(cell.get("outputs", [])):
|
||||
if out.output_type not in {"display_data", "execute_result"}:
|
||||
continue
|
||||
if "text/html" in out.data:
|
||||
out["data"]["text/html"] = dedent(out["data"]["text/html"])
|
||||
# Get the output in data formats that the template needs extracted
|
||||
for mime_type in self.extract_output_types:
|
||||
if mime_type in out.data:
|
||||
data = out.data[mime_type]
|
||||
|
||||
# Binary files are base64-encoded, SVG is already XML
|
||||
if mime_type in {"image/png", "image/jpeg", "application/pdf"}:
|
||||
# data is b64-encoded as text (str, unicode),
|
||||
# we want the original bytes
|
||||
data = a2b_base64(data)
|
||||
elif mime_type == "application/json" or not isinstance(data, str):
|
||||
# Data is either JSON-like and was parsed into a Python
|
||||
# object according to the spec, or data is for sure
|
||||
# JSON. In the latter case we want to go extra sure that
|
||||
# we enclose a scalar string value into extra quotes by
|
||||
# serializing it properly.
|
||||
if isinstance(data, bytes):
|
||||
# We need to guess the encoding in this
|
||||
# instance. Some modules that return raw data like
|
||||
# svg can leave the data in byte form instead of str
|
||||
data = data.decode("utf-8")
|
||||
data = platform_utf_8_encode(json.dumps(data))
|
||||
else:
|
||||
# All other text_type data will fall into this path
|
||||
data = platform_utf_8_encode(data)
|
||||
|
||||
ext = guess_extension_without_jpe(mime_type)
|
||||
if ext is None:
|
||||
ext = "." + mime_type.rsplit("/")[-1]
|
||||
if out.metadata.get("filename", ""):
|
||||
filename = out.metadata["filename"]
|
||||
if not filename.endswith(ext):
|
||||
filename += ext
|
||||
else:
|
||||
filename = self.output_filename_template.format(
|
||||
unique_key=unique_key, cell_index=cell_index, index=index, extension=ext
|
||||
)
|
||||
|
||||
# On the cell, make the figure available via
|
||||
# cell.outputs[i].metadata.filenames['mime/type']
|
||||
# where
|
||||
# cell.outputs[i].data['mime/type'] contains the data
|
||||
if output_files_dir is not None:
|
||||
filename = os.path.join(output_files_dir, filename)
|
||||
out.metadata.setdefault("filenames", {})
|
||||
out.metadata["filenames"][mime_type] = filename
|
||||
|
||||
if filename in resources["outputs"]:
|
||||
raise ValueError(
|
||||
"Your outputs have filename metadata associated "
|
||||
"with them. Nbconvert saves these outputs to "
|
||||
"external files using this filename metadata. "
|
||||
"Filenames need to be unique across the notebook, "
|
||||
"or images will be overwritten. The filename {} is "
|
||||
"associated with more than one output. The second "
|
||||
"output associated with this filename is in cell "
|
||||
"{}.".format(filename, cell_index)
|
||||
)
|
||||
# In the resources, make the figure available via
|
||||
# resources['outputs']['filename'] = data
|
||||
resources["outputs"][filename] = data
|
||||
|
||||
return cell, resources
|
@ -0,0 +1,103 @@
|
||||
"""This preprocessor detect cells using a different language through
|
||||
magic extensions such as `%%R` or `%%octave`. Cell's metadata is marked
|
||||
so that the appropriate highlighter can be used in the `highlight`
|
||||
filter.
|
||||
"""
|
||||
|
||||
# Copyright (c) Jupyter Development Team.
|
||||
# Distributed under the terms of the Modified BSD License.
|
||||
|
||||
|
||||
import re
|
||||
|
||||
from traitlets import Dict
|
||||
|
||||
from .base import Preprocessor
|
||||
|
||||
|
||||
class HighlightMagicsPreprocessor(Preprocessor):
|
||||
"""
|
||||
Detects and tags code cells that use a different languages than Python.
|
||||
"""
|
||||
|
||||
# list of magic language extensions and their associated pygment lexers
|
||||
default_languages = Dict(
|
||||
{
|
||||
"%%R": "r",
|
||||
"%%bash": "bash",
|
||||
"%%cython": "cython",
|
||||
"%%javascript": "javascript",
|
||||
"%%julia": "julia",
|
||||
"%%latex": "latex",
|
||||
"%%octave": "octave",
|
||||
"%%perl": "perl",
|
||||
"%%ruby": "ruby",
|
||||
"%%sh": "sh",
|
||||
"%%sql": "sql",
|
||||
}
|
||||
)
|
||||
|
||||
# user defined language extensions
|
||||
languages = Dict(
|
||||
help=(
|
||||
"Syntax highlighting for magic's extension languages. "
|
||||
"Each item associates a language magic extension such as %%R, "
|
||||
"with a pygments lexer such as r."
|
||||
)
|
||||
).tag(config=True)
|
||||
|
||||
def __init__(self, config=None, **kw):
|
||||
"""Public constructor"""
|
||||
|
||||
super().__init__(config=config, **kw)
|
||||
|
||||
# Update the default languages dict with the user configured ones
|
||||
self.default_languages.update(self.languages)
|
||||
|
||||
# build a regular expression to catch language extensions and choose
|
||||
# an adequate pygments lexer
|
||||
any_language = "|".join(self.default_languages.keys())
|
||||
self.re_magic_language = re.compile(rf"^\s*({any_language})\s+")
|
||||
|
||||
def which_magic_language(self, source):
|
||||
"""
|
||||
When a cell uses another language through a magic extension,
|
||||
the other language is returned.
|
||||
If no language magic is detected, this function returns None.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
source: str
|
||||
Source code of the cell to highlight
|
||||
"""
|
||||
|
||||
m = self.re_magic_language.match(source)
|
||||
|
||||
if m:
|
||||
# By construction of the re, the matched language must be in the
|
||||
# languages dictionary
|
||||
return self.default_languages[m.group(1)]
|
||||
else:
|
||||
return None
|
||||
|
||||
def preprocess_cell(self, cell, resources, cell_index):
|
||||
"""
|
||||
Tags cells using a magic extension language
|
||||
|
||||
Parameters
|
||||
----------
|
||||
cell : NotebookNode cell
|
||||
Notebook cell being processed
|
||||
resources : dictionary
|
||||
Additional resources used in the conversion process. Allows
|
||||
preprocessors to pass variables into the Jinja engine.
|
||||
cell_index : int
|
||||
Index of the cell being processed (see base.py)
|
||||
"""
|
||||
|
||||
# Only tag code cells
|
||||
if cell.cell_type == "code":
|
||||
magic_language = self.which_magic_language(cell.source)
|
||||
if magic_language:
|
||||
cell["metadata"]["magics_language"] = magic_language
|
||||
return cell, resources
|
54
.venv/Lib/site-packages/nbconvert/preprocessors/latex.py
Normal file
54
.venv/Lib/site-packages/nbconvert/preprocessors/latex.py
Normal file
@ -0,0 +1,54 @@
|
||||
"""Module that allows latex output notebooks to be conditioned before
|
||||
they are converted.
|
||||
"""
|
||||
# -----------------------------------------------------------------------------
|
||||
# Copyright (c) 2013, the IPython Development Team.
|
||||
#
|
||||
# Distributed under the terms of the Modified BSD License.
|
||||
#
|
||||
# The full license is in the file COPYING.txt, distributed with this software.
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# Imports
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
|
||||
from traitlets import Unicode
|
||||
|
||||
from .base import Preprocessor
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# Classes
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
|
||||
class LatexPreprocessor(Preprocessor):
|
||||
"""Preprocessor for latex destined documents.
|
||||
|
||||
Mainly populates the ``latex`` key in the resources dict,
|
||||
adding definitions for pygments highlight styles.
|
||||
"""
|
||||
|
||||
style = Unicode("default", help="Name of the pygments style to use").tag(config=True)
|
||||
|
||||
def preprocess(self, nb, resources):
|
||||
"""Preprocessing to apply on each notebook.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
nb : NotebookNode
|
||||
Notebook being converted
|
||||
resources : dictionary
|
||||
Additional resources used in the conversion process. Allows
|
||||
preprocessors to pass variables into the Jinja engine.
|
||||
"""
|
||||
# Generate Pygments definitions for Latex
|
||||
from pygments.formatters import LatexFormatter
|
||||
|
||||
resources.setdefault("latex", {})
|
||||
resources["latex"].setdefault(
|
||||
"pygments_definitions", LatexFormatter(style=self.style).get_style_defs()
|
||||
)
|
||||
resources["latex"].setdefault("pygments_style_name", self.style)
|
||||
return nb, resources
|
@ -0,0 +1,70 @@
|
||||
"""
|
||||
Module containing a preprocessor that removes cells if they match
|
||||
one or more regular expression.
|
||||
"""
|
||||
|
||||
# Copyright (c) IPython Development Team.
|
||||
# Distributed under the terms of the Modified BSD License.
|
||||
|
||||
import re
|
||||
|
||||
from traitlets import List, Unicode
|
||||
|
||||
from .base import Preprocessor
|
||||
|
||||
|
||||
class RegexRemovePreprocessor(Preprocessor):
|
||||
"""
|
||||
Removes cells from a notebook that match one or more regular expression.
|
||||
|
||||
For each cell, the preprocessor checks whether its contents match
|
||||
the regular expressions in the ``patterns`` traitlet which is a list
|
||||
of unicode strings. If the contents match any of the patterns, the cell
|
||||
is removed from the notebook.
|
||||
|
||||
To modify the list of matched patterns,
|
||||
modify the patterns traitlet. For example, execute the following command
|
||||
to convert a notebook to html and remove cells containing only whitespace::
|
||||
|
||||
jupyter nbconvert --RegexRemovePreprocessor.patterns="['\\s*\\Z']" mynotebook.ipynb
|
||||
|
||||
The command line argument
|
||||
sets the list of patterns to ``'\\s*\\Z'`` which matches an arbitrary number
|
||||
of whitespace characters followed by the end of the string.
|
||||
|
||||
See https://regex101.com/ for an interactive guide to regular expressions
|
||||
(make sure to select the python flavor). See
|
||||
https://docs.python.org/library/re.html for the official regular expression
|
||||
documentation in python.
|
||||
"""
|
||||
|
||||
patterns = List(Unicode(), default_value=[]).tag(config=True)
|
||||
|
||||
def check_conditions(self, cell):
|
||||
"""
|
||||
Checks that a cell matches the pattern.
|
||||
|
||||
Returns: Boolean.
|
||||
True means cell should *not* be removed.
|
||||
"""
|
||||
|
||||
# Compile all the patterns into one: each pattern is first wrapped
|
||||
# by a non-capturing group to ensure the correct order of precedence
|
||||
# and the patterns are joined with a logical or
|
||||
pattern = re.compile("|".join("(?:%s)" % pattern for pattern in self.patterns))
|
||||
|
||||
# Filter out cells that meet the pattern and have no outputs
|
||||
return not pattern.match(cell.source)
|
||||
|
||||
def preprocess(self, nb, resources):
|
||||
"""
|
||||
Preprocessing to apply to each notebook. See base.py for details.
|
||||
"""
|
||||
# Skip preprocessing if the list of patterns is empty
|
||||
if not self.patterns:
|
||||
return nb, resources
|
||||
|
||||
# Filter out cells that meet the conditions
|
||||
nb.cells = [cell for cell in nb.cells if self.check_conditions(cell)]
|
||||
|
||||
return nb, resources
|
169
.venv/Lib/site-packages/nbconvert/preprocessors/sanitize.py
Normal file
169
.venv/Lib/site-packages/nbconvert/preprocessors/sanitize.py
Normal file
@ -0,0 +1,169 @@
|
||||
"""
|
||||
NBConvert Preprocessor for sanitizing HTML rendering of notebooks.
|
||||
"""
|
||||
|
||||
import warnings
|
||||
|
||||
from bleach import ALLOWED_ATTRIBUTES, ALLOWED_TAGS, clean
|
||||
from traitlets import Any, Bool, List, Set, Unicode
|
||||
|
||||
_USE_BLEACH_CSS_SANITIZER = False
|
||||
_USE_BLEACH_STYLES = False
|
||||
|
||||
|
||||
try:
|
||||
# bleach[css] >=5.0
|
||||
from bleach.css_sanitizer import ALLOWED_CSS_PROPERTIES as ALLOWED_STYLES
|
||||
from bleach.css_sanitizer import CSSSanitizer
|
||||
|
||||
_USE_BLEACH_CSS_SANITIZER = True
|
||||
_USE_BLEACH_STYLES = False
|
||||
except ImportError:
|
||||
try:
|
||||
# bleach <5
|
||||
from bleach import ALLOWED_STYLES
|
||||
|
||||
_USE_BLEACH_CSS_SANITIZER = False
|
||||
_USE_BLEACH_STYLES = True
|
||||
warnings.warn(
|
||||
"Support for bleach <5 will be removed in a future version of nbconvert",
|
||||
DeprecationWarning,
|
||||
)
|
||||
|
||||
except ImportError:
|
||||
warnings.warn(
|
||||
"The installed bleach/tinycss2 do not provide CSS sanitization, "
|
||||
"please upgrade to bleach >=5",
|
||||
UserWarning,
|
||||
)
|
||||
|
||||
|
||||
from .base import Preprocessor
|
||||
|
||||
__all__ = ["SanitizeHTML"]
|
||||
|
||||
|
||||
class SanitizeHTML(Preprocessor):
|
||||
|
||||
# Bleach config.
|
||||
attributes = Any(
|
||||
config=True,
|
||||
default_value=ALLOWED_ATTRIBUTES,
|
||||
help="Allowed HTML tag attributes",
|
||||
)
|
||||
tags = List(
|
||||
Unicode(),
|
||||
config=True,
|
||||
default_value=ALLOWED_TAGS,
|
||||
help="List of HTML tags to allow",
|
||||
)
|
||||
styles = List(
|
||||
Unicode(),
|
||||
config=True,
|
||||
default_value=ALLOWED_STYLES,
|
||||
help="Allowed CSS styles if <style> tag is allowed",
|
||||
)
|
||||
strip = Bool(
|
||||
config=True,
|
||||
default_value=False,
|
||||
help="If True, remove unsafe markup entirely instead of escaping",
|
||||
)
|
||||
strip_comments = Bool(
|
||||
config=True,
|
||||
default_value=True,
|
||||
help="If True, strip comments from escaped HTML",
|
||||
)
|
||||
|
||||
# Display data config.
|
||||
safe_output_keys = Set(
|
||||
config=True,
|
||||
default_value={
|
||||
"metadata", # Not a mimetype per-se, but expected and safe.
|
||||
"text/plain",
|
||||
"text/latex",
|
||||
"application/json",
|
||||
"image/png",
|
||||
"image/jpeg",
|
||||
},
|
||||
help="Cell output mimetypes to render without modification",
|
||||
)
|
||||
sanitized_output_types = Set(
|
||||
config=True,
|
||||
default_value={
|
||||
"text/html",
|
||||
"text/markdown",
|
||||
},
|
||||
help="Cell output types to display after escaping with Bleach.",
|
||||
)
|
||||
|
||||
def preprocess_cell(self, cell, resources, cell_index):
|
||||
"""
|
||||
Sanitize potentially-dangerous contents of the cell.
|
||||
|
||||
Cell Types:
|
||||
raw:
|
||||
Sanitize literal HTML
|
||||
markdown:
|
||||
Sanitize literal HTML
|
||||
code:
|
||||
Sanitize outputs that could result in code execution
|
||||
"""
|
||||
if cell.cell_type == "raw":
|
||||
# Sanitize all raw cells anyway.
|
||||
# Only ones with the text/html mimetype should be emitted
|
||||
# but erring on the side of safety maybe.
|
||||
cell.source = self.sanitize_html_tags(cell.source)
|
||||
return cell, resources
|
||||
elif cell.cell_type == "markdown":
|
||||
cell.source = self.sanitize_html_tags(cell.source)
|
||||
return cell, resources
|
||||
elif cell.cell_type == "code":
|
||||
cell.outputs = self.sanitize_code_outputs(cell.outputs)
|
||||
return cell, resources
|
||||
|
||||
def sanitize_code_outputs(self, outputs):
|
||||
"""
|
||||
Sanitize code cell outputs.
|
||||
|
||||
Removes 'text/javascript' fields from display_data outputs, and
|
||||
runs `sanitize_html_tags` over 'text/html'.
|
||||
"""
|
||||
for output in outputs:
|
||||
# These are always ascii, so nothing to escape.
|
||||
if output["output_type"] in ("stream", "error"):
|
||||
continue
|
||||
data = output.data
|
||||
to_remove = []
|
||||
for key in data:
|
||||
if key in self.safe_output_keys:
|
||||
continue
|
||||
elif key in self.sanitized_output_types:
|
||||
self.log.info("Sanitizing %s" % key)
|
||||
data[key] = self.sanitize_html_tags(data[key])
|
||||
else:
|
||||
# Mark key for removal. (Python doesn't allow deletion of
|
||||
# keys from a dict during iteration)
|
||||
to_remove.append(key)
|
||||
for key in to_remove:
|
||||
self.log.info("Removing %s" % key)
|
||||
del data[key]
|
||||
return outputs
|
||||
|
||||
def sanitize_html_tags(self, html_str):
|
||||
"""
|
||||
Sanitize a string containing raw HTML tags.
|
||||
"""
|
||||
kwargs = dict(
|
||||
tags=self.tags,
|
||||
attributes=self.attributes,
|
||||
strip=self.strip,
|
||||
strip_comments=self.strip_comments,
|
||||
)
|
||||
|
||||
if _USE_BLEACH_CSS_SANITIZER:
|
||||
css_sanitizer = CSSSanitizer(allowed_css_properties=self.styles)
|
||||
kwargs.update(css_sanitizer=css_sanitizer)
|
||||
elif _USE_BLEACH_STYLES:
|
||||
kwargs.update(styles=self.styles)
|
||||
|
||||
return clean(html_str, **kwargs)
|
153
.venv/Lib/site-packages/nbconvert/preprocessors/svg2pdf.py
Normal file
153
.venv/Lib/site-packages/nbconvert/preprocessors/svg2pdf.py
Normal file
@ -0,0 +1,153 @@
|
||||
"""Module containing a preprocessor that converts outputs in the notebook from
|
||||
one format to another.
|
||||
"""
|
||||
|
||||
# Copyright (c) Jupyter Development Team.
|
||||
# Distributed under the terms of the Modified BSD License.
|
||||
|
||||
import base64
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
from shutil import which
|
||||
from tempfile import TemporaryDirectory
|
||||
|
||||
from traitlets import List, Unicode, Union, default
|
||||
|
||||
from ..utils.io import FormatSafeDict
|
||||
from .convertfigures import ConvertFiguresPreprocessor
|
||||
|
||||
# inkscape path for darwin (macOS)
|
||||
INKSCAPE_APP = "/Applications/Inkscape.app/Contents/Resources/bin/inkscape"
|
||||
# Recent versions of Inkscape (v1.0) moved the executable from
|
||||
# Resources/bin/inkscape to MacOS/inkscape
|
||||
INKSCAPE_APP_v1 = "/Applications/Inkscape.app/Contents/MacOS/inkscape"
|
||||
|
||||
if sys.platform == "win32":
|
||||
try:
|
||||
import winreg
|
||||
except ImportError:
|
||||
import _winreg as winreg
|
||||
|
||||
|
||||
class SVG2PDFPreprocessor(ConvertFiguresPreprocessor):
|
||||
"""
|
||||
Converts all of the outputs in a notebook from SVG to PDF.
|
||||
"""
|
||||
|
||||
@default("from_format")
|
||||
def _from_format_default(self):
|
||||
return "image/svg+xml"
|
||||
|
||||
@default("to_format")
|
||||
def _to_format_default(self):
|
||||
return "application/pdf"
|
||||
|
||||
inkscape_version = Unicode(
|
||||
help="""The version of inkscape being used.
|
||||
|
||||
This affects how the conversion command is run.
|
||||
"""
|
||||
).tag(config=True)
|
||||
|
||||
@default("inkscape_version")
|
||||
def _inkscape_version_default(self):
|
||||
p = subprocess.Popen(
|
||||
[self.inkscape, "--version"], stdout=subprocess.PIPE, stderr=subprocess.PIPE
|
||||
)
|
||||
output, _ = p.communicate()
|
||||
if p.returncode != 0:
|
||||
raise RuntimeError("Unable to find inkscape executable --version")
|
||||
return output.decode("utf-8").split(" ")[1]
|
||||
|
||||
# FIXME: Deprecate passing a string here
|
||||
command = Union(
|
||||
[Unicode(), List()],
|
||||
help="""
|
||||
The command to use for converting SVG to PDF
|
||||
|
||||
This traitlet is a template, which will be formatted with the keys
|
||||
to_filename and from_filename.
|
||||
|
||||
The conversion call must read the SVG from {from_filename},
|
||||
and write a PDF to {to_filename}.
|
||||
|
||||
It could be a List (recommended) or a String. If string, it will
|
||||
be passed to a shell for execution.
|
||||
""",
|
||||
).tag(config=True)
|
||||
|
||||
@default("command")
|
||||
def _command_default(self):
|
||||
major_version = self.inkscape_version.split(".")[0]
|
||||
command = [self.inkscape]
|
||||
|
||||
if int(major_version) < 1:
|
||||
# --without-gui is only needed for inkscape 0.x
|
||||
command.append("--without-gui")
|
||||
# --export-pdf is old name for --export-filename
|
||||
command.append("--export-pdf={to_filename}")
|
||||
else:
|
||||
command.append("--export-filename={to_filename}")
|
||||
|
||||
command.append("{from_filename}")
|
||||
return command
|
||||
|
||||
inkscape = Unicode(help="The path to Inkscape, if necessary").tag(config=True)
|
||||
|
||||
@default("inkscape")
|
||||
def _inkscape_default(self):
|
||||
inkscape_path = which("inkscape")
|
||||
if inkscape_path is not None:
|
||||
return inkscape_path
|
||||
if sys.platform == "darwin":
|
||||
if os.path.isfile(INKSCAPE_APP_v1):
|
||||
return INKSCAPE_APP_v1
|
||||
# Order is important. If INKSCAPE_APP exists, prefer it over
|
||||
# the executable in the MacOS directory.
|
||||
if os.path.isfile(INKSCAPE_APP):
|
||||
return INKSCAPE_APP
|
||||
if sys.platform == "win32":
|
||||
wr_handle = winreg.ConnectRegistry(None, winreg.HKEY_LOCAL_MACHINE)
|
||||
try:
|
||||
rkey = winreg.OpenKey(wr_handle, "SOFTWARE\\Classes\\inkscape.svg\\DefaultIcon")
|
||||
inkscape = winreg.QueryValueEx(rkey, "")[0]
|
||||
except FileNotFoundError:
|
||||
raise FileNotFoundError("Inkscape executable not found")
|
||||
return inkscape
|
||||
return "inkscape"
|
||||
|
||||
def convert_figure(self, data_format, data):
|
||||
"""
|
||||
Convert a single SVG figure to PDF. Returns converted data.
|
||||
"""
|
||||
|
||||
# Work in a temporary directory
|
||||
with TemporaryDirectory() as tmpdir:
|
||||
|
||||
# Write fig to temp file
|
||||
input_filename = os.path.join(tmpdir, "figure.svg")
|
||||
# SVG data is unicode text
|
||||
with open(input_filename, "w", encoding="utf8") as f:
|
||||
f.write(data)
|
||||
|
||||
# Call conversion application
|
||||
output_filename = os.path.join(tmpdir, "figure.pdf")
|
||||
|
||||
template_vars = {"from_filename": input_filename, "to_filename": output_filename}
|
||||
if isinstance(self.command, list):
|
||||
full_cmd = [s.format_map(FormatSafeDict(**template_vars)) for s in self.command]
|
||||
else:
|
||||
# For backwards compatibility with specifying strings
|
||||
# Okay-ish, since the string is trusted
|
||||
full_cmd = self.command.format(*template_vars)
|
||||
subprocess.call(full_cmd, shell=isinstance(full_cmd, str))
|
||||
|
||||
# Read output from drive
|
||||
# return value expects a filename
|
||||
if os.path.isfile(output_filename):
|
||||
with open(output_filename, "rb") as f:
|
||||
# PDF is a nb supported binary, data type, so base64 encode.
|
||||
return base64.encodebytes(f.read())
|
||||
else:
|
||||
raise TypeError("Inkscape svg to pdf conversion failed")
|
141
.venv/Lib/site-packages/nbconvert/preprocessors/tagremove.py
Normal file
141
.venv/Lib/site-packages/nbconvert/preprocessors/tagremove.py
Normal file
@ -0,0 +1,141 @@
|
||||
"""
|
||||
Module containing a preprocessor that removes cells if they match
|
||||
one or more regular expression.
|
||||
"""
|
||||
|
||||
# Copyright (c) IPython Development Team.
|
||||
# Distributed under the terms of the Modified BSD License.
|
||||
|
||||
from traitlets import Set, Unicode
|
||||
|
||||
from .base import Preprocessor
|
||||
|
||||
|
||||
class TagRemovePreprocessor(Preprocessor):
|
||||
"""
|
||||
Removes inputs, outputs, or cells from a notebook that
|
||||
have tags that designate they are to be removed prior to exporting
|
||||
the notebook.
|
||||
|
||||
remove_cell_tags
|
||||
removes cells tagged with these values
|
||||
|
||||
remove_all_outputs_tags
|
||||
removes entire output areas on cells
|
||||
tagged with these values
|
||||
|
||||
remove_single_output_tags
|
||||
removes individual output objects on
|
||||
outputs tagged with these values
|
||||
|
||||
remove_input_tags
|
||||
removes inputs tagged with these values
|
||||
"""
|
||||
|
||||
remove_cell_tags = Set(
|
||||
Unicode(),
|
||||
default_value=[],
|
||||
help=(
|
||||
"Tags indicating which cells are to be removed,"
|
||||
"matches tags in ``cell.metadata.tags``."
|
||||
),
|
||||
).tag(config=True)
|
||||
remove_all_outputs_tags = Set(
|
||||
Unicode(),
|
||||
default_value=[],
|
||||
help=(
|
||||
"Tags indicating cells for which the outputs are to be removed,"
|
||||
"matches tags in ``cell.metadata.tags``."
|
||||
),
|
||||
).tag(config=True)
|
||||
remove_single_output_tags = Set(
|
||||
Unicode(),
|
||||
default_value=[],
|
||||
help=(
|
||||
"Tags indicating which individual outputs are to be removed,"
|
||||
"matches output *i* tags in ``cell.outputs[i].metadata.tags``."
|
||||
),
|
||||
).tag(config=True)
|
||||
remove_input_tags = Set(
|
||||
Unicode(),
|
||||
default_value=[],
|
||||
help=(
|
||||
"Tags indicating cells for which input is to be removed,"
|
||||
"matches tags in ``cell.metadata.tags``."
|
||||
),
|
||||
).tag(config=True)
|
||||
remove_metadata_fields = Set({"collapsed", "scrolled"}).tag(config=True)
|
||||
|
||||
def check_cell_conditions(self, cell, resources, index):
|
||||
"""
|
||||
Checks that a cell has a tag that is to be removed
|
||||
|
||||
Returns: Boolean.
|
||||
True means cell should *not* be removed.
|
||||
"""
|
||||
|
||||
# Return true if any of the tags in the cell are removable.
|
||||
return not self.remove_cell_tags.intersection(cell.get("metadata", {}).get("tags", []))
|
||||
|
||||
def preprocess(self, nb, resources):
|
||||
"""
|
||||
Preprocessing to apply to each notebook. See base.py for details.
|
||||
"""
|
||||
# Skip preprocessing if the list of patterns is empty
|
||||
if not any(
|
||||
[
|
||||
self.remove_cell_tags,
|
||||
self.remove_all_outputs_tags,
|
||||
self.remove_single_output_tags,
|
||||
self.remove_input_tags,
|
||||
]
|
||||
):
|
||||
return nb, resources
|
||||
|
||||
# Filter out cells that meet the conditions
|
||||
nb.cells = [
|
||||
self.preprocess_cell(cell, resources, index)[0]
|
||||
for index, cell in enumerate(nb.cells)
|
||||
if self.check_cell_conditions(cell, resources, index)
|
||||
]
|
||||
|
||||
return nb, resources
|
||||
|
||||
def preprocess_cell(self, cell, resources, cell_index):
|
||||
"""
|
||||
Apply a transformation on each cell. See base.py for details.
|
||||
"""
|
||||
|
||||
if (
|
||||
self.remove_all_outputs_tags.intersection(cell.get("metadata", {}).get("tags", []))
|
||||
and cell.cell_type == "code"
|
||||
):
|
||||
|
||||
cell.outputs = []
|
||||
cell.execution_count = None
|
||||
# Remove metadata associated with output
|
||||
if "metadata" in cell:
|
||||
for field in self.remove_metadata_fields:
|
||||
cell.metadata.pop(field, None)
|
||||
|
||||
if self.remove_input_tags.intersection(cell.get("metadata", {}).get("tags", [])):
|
||||
cell.transient = {"remove_source": True}
|
||||
|
||||
if cell.get("outputs", []):
|
||||
cell.outputs = [
|
||||
output
|
||||
for output_index, output in enumerate(cell.outputs)
|
||||
if self.check_output_conditions(output, resources, cell_index, output_index)
|
||||
]
|
||||
return cell, resources
|
||||
|
||||
def check_output_conditions(self, output, resources, cell_index, output_index):
|
||||
"""
|
||||
Checks that an output has a tag that indicates removal.
|
||||
|
||||
Returns: Boolean.
|
||||
True means output should *not* be removed.
|
||||
"""
|
||||
return not self.remove_single_output_tags.intersection(
|
||||
output.get("metadata", {}).get("tags", [])
|
||||
)
|
@ -0,0 +1,52 @@
|
||||
"""utility functions for preprocessor tests"""
|
||||
|
||||
# Copyright (c) IPython Development Team.
|
||||
# Distributed under the terms of the Modified BSD License.
|
||||
|
||||
from nbformat import v4 as nbformat
|
||||
|
||||
from ...exporters.exporter import ResourcesDict
|
||||
from ...tests.base import TestsBase
|
||||
|
||||
|
||||
class PreprocessorTestsBase(TestsBase):
|
||||
"""Contains test functions preprocessor tests"""
|
||||
|
||||
def build_notebook(self, with_json_outputs=False):
|
||||
"""Build a notebook in memory for use with preprocessor tests"""
|
||||
|
||||
outputs = [
|
||||
nbformat.new_output("stream", name="stdout", text="a"),
|
||||
nbformat.new_output("display_data", data={"text/plain": "b"}),
|
||||
nbformat.new_output("stream", name="stdout", text="c"),
|
||||
nbformat.new_output("stream", name="stdout", text="d"),
|
||||
nbformat.new_output("stream", name="stderr", text="e"),
|
||||
nbformat.new_output("stream", name="stderr", text="f"),
|
||||
nbformat.new_output("display_data", data={"image/png": "Zw=="}), # g
|
||||
nbformat.new_output("display_data", data={"application/pdf": "aA=="}), # h
|
||||
]
|
||||
if with_json_outputs:
|
||||
outputs.extend(
|
||||
[
|
||||
nbformat.new_output("display_data", data={"application/json": [1, 2, 3]}), # j
|
||||
nbformat.new_output(
|
||||
"display_data", data={"application/json": {"a": 1, "c": {"b": 2}}}
|
||||
), # k
|
||||
nbformat.new_output("display_data", data={"application/json": "abc"}), # l
|
||||
nbformat.new_output("display_data", data={"application/json": 15.03}), # m
|
||||
]
|
||||
)
|
||||
|
||||
cells = [
|
||||
nbformat.new_code_cell(source="$ e $", execution_count=1, outputs=outputs),
|
||||
nbformat.new_markdown_cell(source="$ e $"),
|
||||
]
|
||||
|
||||
return nbformat.new_notebook(cells=cells)
|
||||
|
||||
def build_resources(self):
|
||||
"""Build an empty resources dictionary."""
|
||||
|
||||
res = ResourcesDict()
|
||||
res["metadata"] = ResourcesDict()
|
||||
return res
|
@ -0,0 +1,24 @@
|
||||
from jupyter_client.manager import KernelManager
|
||||
|
||||
|
||||
class FakeCustomKernelManager(KernelManager):
|
||||
expected_methods = {
|
||||
"__init__": 0,
|
||||
"client": 0,
|
||||
"start_kernel": 0,
|
||||
}
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.log.info("FakeCustomKernelManager initialized")
|
||||
self.expected_methods["__init__"] += 1
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
def start_kernel(self, *args, **kwargs):
|
||||
self.log.info("FakeCustomKernelManager started a kernel")
|
||||
self.expected_methods["start_kernel"] += 1
|
||||
return super().start_kernel(*args, **kwargs)
|
||||
|
||||
def client(self, *args, **kwargs):
|
||||
self.log.info("FakeCustomKernelManager created a client")
|
||||
self.expected_methods["client"] += 1
|
||||
return super().client(*args, **kwargs)
|
@ -0,0 +1,26 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 1,
|
||||
"metadata": {
|
||||
"collapsed": false
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Hello World\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"print(\"Hello World\")"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 0
|
||||
}
|
@ -0,0 +1,49 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 1,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"this is a code cell\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"print('this is a code cell')"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# This is a markdown cell"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3",
|
||||
"language": "python",
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"codemirror_mode": {
|
||||
"name": "ipython",
|
||||
"version": 3
|
||||
},
|
||||
"file_extension": ".py",
|
||||
"mimetype": "text/x-python",
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.8.2"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 4
|
||||
}
|
@ -0,0 +1,134 @@
|
||||
"""
|
||||
Module with tests for the clearmetadata preprocessor.
|
||||
"""
|
||||
|
||||
# Copyright (c) IPython Development Team.
|
||||
# Distributed under the terms of the Modified BSD License.
|
||||
|
||||
from ..clearmetadata import ClearMetadataPreprocessor
|
||||
from .base import PreprocessorTestsBase
|
||||
|
||||
|
||||
class TestClearMetadata(PreprocessorTestsBase):
|
||||
"""Contains test functions for clearmetadata.py"""
|
||||
|
||||
def build_notebook(self):
|
||||
notebook = super().build_notebook()
|
||||
notebook.metadata = {
|
||||
"language_info": {"name": "python", "version": "3.6.7"},
|
||||
"kernelspec": {"language": "python", "name": "python3"},
|
||||
}
|
||||
# Add a test field to the first cell
|
||||
if "metadata" not in notebook.cells[0]:
|
||||
notebook.cells[0].metadata = {}
|
||||
notebook.cells[0].metadata["test_field"] = "test_value"
|
||||
notebook.cells[0].metadata["test_nested"] = {"test_keep": "keep", "test_filtered": "filter"}
|
||||
notebook.cells[0].metadata["executeTime"] = dict(
|
||||
[("end_time", "09:31:50"), ("start_time", "09:31:49")]
|
||||
)
|
||||
return notebook
|
||||
|
||||
def build_preprocessor(self, **kwargs):
|
||||
"""Make an instance of a preprocessor"""
|
||||
preprocessor = ClearMetadataPreprocessor(**kwargs)
|
||||
preprocessor.enabled = True
|
||||
return preprocessor
|
||||
|
||||
def test_constructor(self):
|
||||
"""Can a ClearMetadataPreprocessor be constructed?"""
|
||||
self.build_preprocessor()
|
||||
|
||||
def test_default_output(self):
|
||||
"""Test the output of the ClearMetadataPreprocessor"""
|
||||
nb = self.build_notebook()
|
||||
res = self.build_resources()
|
||||
preprocessor = self.build_preprocessor()
|
||||
nb, res = preprocessor(nb, res)
|
||||
|
||||
assert not nb.cells[0].metadata
|
||||
# By default we only perserve the langauge name
|
||||
assert nb.metadata == {"language_info": {"name": "python"}}
|
||||
|
||||
def test_cell_only(self):
|
||||
"""Test the output of the ClearMetadataPreprocessor"""
|
||||
nb = self.build_notebook()
|
||||
res = self.build_resources()
|
||||
preprocessor = self.build_preprocessor(clear_notebook_metadata=False)
|
||||
nb, res = preprocessor(nb, res)
|
||||
|
||||
assert not nb.cells[0].metadata
|
||||
assert nb.metadata
|
||||
|
||||
def test_notebook_only(self):
|
||||
"""Test the output of the ClearMetadataPreprocessor"""
|
||||
nb = self.build_notebook()
|
||||
res = self.build_resources()
|
||||
preprocessor = self.build_preprocessor(
|
||||
clear_cell_metadata=False, preserve_nb_metadata_mask=set()
|
||||
)
|
||||
nb, res = preprocessor(nb, res)
|
||||
|
||||
assert nb.cells[0].metadata
|
||||
assert not nb.metadata
|
||||
|
||||
def test_selective_cell_metadata(self):
|
||||
"""Test the output of the ClearMetadataPreprocessor"""
|
||||
nb = self.build_notebook()
|
||||
res = self.build_resources()
|
||||
preprocessor = self.build_preprocessor(
|
||||
preserve_cell_metadata_mask=["test_field"], preserve_nb_metadata_mask=set()
|
||||
)
|
||||
nb, res = preprocessor(nb, res)
|
||||
|
||||
assert nb.cells[0].metadata == {"test_field": "test_value"}
|
||||
assert not nb.metadata
|
||||
|
||||
def test_selective_cell_tuple_metadata(self):
|
||||
"""Test the output of the ClearMetadataPreprocessor"""
|
||||
nb = self.build_notebook()
|
||||
res = self.build_resources()
|
||||
# Ensure that a tuple length 1 works as well as a string key
|
||||
preprocessor = self.build_preprocessor(
|
||||
preserve_cell_metadata_mask=[("test_field",)], preserve_nb_metadata_mask=set()
|
||||
)
|
||||
nb, res = preprocessor(nb, res)
|
||||
|
||||
assert nb.cells[0].metadata == {"test_field": "test_value"}
|
||||
assert not nb.metadata
|
||||
|
||||
def test_nested_cell_metadata(self):
|
||||
"""Test the output of the ClearMetadataPreprocessor"""
|
||||
nb = self.build_notebook()
|
||||
res = self.build_resources()
|
||||
preprocessor = self.build_preprocessor(
|
||||
preserve_cell_metadata_mask=[("test_nested", "test_keep")],
|
||||
preserve_nb_metadata_mask=set(),
|
||||
)
|
||||
nb, res = preprocessor(nb, res)
|
||||
|
||||
assert nb.cells[0].metadata == {"test_nested": {"test_keep": "keep"}}
|
||||
assert not nb.metadata
|
||||
|
||||
def test_nested_cell_tuple_metadata(self):
|
||||
"""Test the output of the ClearMetadataPreprocessor"""
|
||||
nb = self.build_notebook()
|
||||
res = self.build_resources()
|
||||
# Ensure that a tuple length 1 works as well as a string key
|
||||
preprocessor = self.build_preprocessor(
|
||||
preserve_cell_metadata_mask=[("test_nested", ("test_keep",))],
|
||||
preserve_nb_metadata_mask=set(),
|
||||
)
|
||||
nb, res = preprocessor(nb, res)
|
||||
|
||||
assert nb.cells[0].metadata == {"test_nested": {"test_keep": "keep"}}
|
||||
assert not nb.metadata
|
||||
|
||||
def test_selective_notebook_metadata(self):
|
||||
"""Test the output of the ClearMetadataPreprocessor"""
|
||||
nb = self.build_notebook()
|
||||
res = self.build_resources()
|
||||
preprocessor = self.build_preprocessor(preserve_nb_metadata_mask=["kernelspec"])
|
||||
nb, res = preprocessor(nb, res)
|
||||
|
||||
assert not nb.cells[0].metadata
|
||||
assert nb.metadata == {"kernelspec": {"language": "python", "name": "python3"}}
|
@ -0,0 +1,49 @@
|
||||
"""
|
||||
Module with tests for the clearoutput preprocessor.
|
||||
"""
|
||||
|
||||
# Copyright (c) IPython Development Team.
|
||||
# Distributed under the terms of the Modified BSD License.
|
||||
|
||||
from ..clearoutput import ClearOutputPreprocessor
|
||||
from .base import PreprocessorTestsBase
|
||||
|
||||
|
||||
class TestClearOutput(PreprocessorTestsBase):
|
||||
"""Contains test functions for clearoutput.py"""
|
||||
|
||||
def build_notebook(self):
|
||||
notebook = super().build_notebook()
|
||||
# Add a test field to the first cell
|
||||
if "metadata" not in notebook.cells[0]:
|
||||
notebook.cells[0].metadata = {}
|
||||
notebook.cells[0].metadata["test_field"] = "test_value"
|
||||
return notebook
|
||||
|
||||
def build_preprocessor(self):
|
||||
"""Make an instance of a preprocessor"""
|
||||
preprocessor = ClearOutputPreprocessor()
|
||||
preprocessor.enabled = True
|
||||
return preprocessor
|
||||
|
||||
def test_constructor(self):
|
||||
"""Can a ClearOutputPreprocessor be constructed?"""
|
||||
self.build_preprocessor()
|
||||
|
||||
def test_output(self):
|
||||
"""Test the output of the ClearOutputPreprocessor"""
|
||||
for remove_test_field in [False, True]:
|
||||
nb = self.build_notebook()
|
||||
res = self.build_resources()
|
||||
preprocessor = self.build_preprocessor()
|
||||
# Also remove the test field in addition to defaults
|
||||
if remove_test_field:
|
||||
preprocessor.remove_metadata_fields.add("test_field")
|
||||
nb, res = preprocessor(nb, res)
|
||||
assert nb.cells[0].outputs == []
|
||||
assert nb.cells[0].execution_count is None
|
||||
if "metadata" in nb.cells[0]:
|
||||
for field in preprocessor.remove_metadata_fields:
|
||||
assert field not in nb.cells[0].metadata
|
||||
# Ensure the test field is only removed when added to the traitlet
|
||||
assert remove_test_field or "test_field" in nb.cells[0].metadata
|
@ -0,0 +1,62 @@
|
||||
"""Tests for the coalescestreams preprocessor"""
|
||||
|
||||
# Copyright (c) IPython Development Team.
|
||||
# Distributed under the terms of the Modified BSD License.
|
||||
|
||||
from nbformat import v4 as nbformat
|
||||
|
||||
from ..coalescestreams import coalesce_streams
|
||||
from .base import PreprocessorTestsBase
|
||||
|
||||
|
||||
class TestCoalesceStreams(PreprocessorTestsBase):
|
||||
"""Contains test functions for coalescestreams.py"""
|
||||
|
||||
def test_coalesce_streams(self):
|
||||
"""coalesce_streams preprocessor output test"""
|
||||
nb = self.build_notebook()
|
||||
res = self.build_resources()
|
||||
nb, res = coalesce_streams(nb, res)
|
||||
outputs = nb.cells[0].outputs
|
||||
self.assertEqual(outputs[0].text, "a")
|
||||
self.assertEqual(outputs[1].output_type, "display_data")
|
||||
self.assertEqual(outputs[2].text, "cd")
|
||||
self.assertEqual(outputs[3].text, "ef")
|
||||
|
||||
def test_coalesce_sequenced_streams(self):
|
||||
"""Can the coalesce streams preprocessor merge a sequence of streams?"""
|
||||
outputs = [
|
||||
nbformat.new_output(output_type="stream", name="stdout", text="0"),
|
||||
nbformat.new_output(output_type="stream", name="stdout", text="1"),
|
||||
nbformat.new_output(output_type="stream", name="stdout", text="2"),
|
||||
nbformat.new_output(output_type="stream", name="stdout", text="3"),
|
||||
nbformat.new_output(output_type="stream", name="stdout", text="4"),
|
||||
nbformat.new_output(output_type="stream", name="stdout", text="5"),
|
||||
nbformat.new_output(output_type="stream", name="stdout", text="6"),
|
||||
nbformat.new_output(output_type="stream", name="stdout", text="7"),
|
||||
]
|
||||
cells = [nbformat.new_code_cell(source="# None", execution_count=1, outputs=outputs)]
|
||||
|
||||
nb = nbformat.new_notebook(cells=cells)
|
||||
res = self.build_resources()
|
||||
nb, res = coalesce_streams(nb, res)
|
||||
outputs = nb.cells[0].outputs
|
||||
self.assertEqual(outputs[0].text, "01234567")
|
||||
|
||||
def test_coalesce_replace_streams(self):
|
||||
"""Are \\r characters handled?"""
|
||||
outputs = [
|
||||
nbformat.new_output(output_type="stream", name="stdout", text="z"),
|
||||
nbformat.new_output(output_type="stream", name="stdout", text="\ra"),
|
||||
nbformat.new_output(output_type="stream", name="stdout", text="\nz\rb"),
|
||||
nbformat.new_output(output_type="stream", name="stdout", text="\nz"),
|
||||
nbformat.new_output(output_type="stream", name="stdout", text="\rc\n"),
|
||||
nbformat.new_output(output_type="stream", name="stdout", text="z\rz\rd"),
|
||||
]
|
||||
cells = [nbformat.new_code_cell(source="# None", execution_count=1, outputs=outputs)]
|
||||
|
||||
nb = nbformat.new_notebook(cells=cells)
|
||||
res = self.build_resources()
|
||||
nb, res = coalesce_streams(nb, res)
|
||||
outputs = nb.cells[0].outputs
|
||||
self.assertEqual(outputs[0].text, "a\nb\nc\nd")
|
@ -0,0 +1,44 @@
|
||||
"""
|
||||
Module with tests for the csshtmlheader preprocessor
|
||||
"""
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# Copyright (c) 2013, the IPython Development Team.
|
||||
#
|
||||
# Distributed under the terms of the Modified BSD License.
|
||||
#
|
||||
# The full license is in the file COPYING.txt, distributed with this software.
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# Imports
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
from ..csshtmlheader import CSSHTMLHeaderPreprocessor
|
||||
from .base import PreprocessorTestsBase
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# Class
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
|
||||
class TestCSSHTMLHeader(PreprocessorTestsBase):
|
||||
"""Contains test functions for csshtmlheader.py"""
|
||||
|
||||
def build_preprocessor(self):
|
||||
"""Make an instance of a preprocessor"""
|
||||
preprocessor = CSSHTMLHeaderPreprocessor()
|
||||
preprocessor.enabled = True
|
||||
return preprocessor
|
||||
|
||||
def test_constructor(self):
|
||||
"""Can a CSSHTMLHeaderPreprocessor be constructed?"""
|
||||
self.build_preprocessor()
|
||||
|
||||
def test_output(self):
|
||||
"""Test the output of the CSSHTMLHeaderPreprocessor"""
|
||||
nb = self.build_notebook()
|
||||
res = self.build_resources()
|
||||
preprocessor = self.build_preprocessor()
|
||||
nb, res = preprocessor(nb, res)
|
||||
assert "css" in res["inlining"]
|
@ -0,0 +1,105 @@
|
||||
"""
|
||||
Module with tests for the execute preprocessor.
|
||||
"""
|
||||
|
||||
# Copyright (c) IPython Development Team.
|
||||
# Distributed under the terms of the Modified BSD License.
|
||||
import os
|
||||
import re
|
||||
from copy import deepcopy
|
||||
|
||||
import nbformat
|
||||
import pytest
|
||||
|
||||
from ..execute import ExecutePreprocessor, executenb
|
||||
|
||||
addr_pat = re.compile(r"0x[0-9a-f]{7,9}")
|
||||
|
||||
|
||||
def normalize_output(output):
|
||||
"""
|
||||
Normalizes (most) outputs for comparison.
|
||||
"""
|
||||
output = dict(output)
|
||||
if "metadata" in output:
|
||||
del output["metadata"]
|
||||
if "text" in output:
|
||||
output["text"] = re.sub(addr_pat, "<HEXADDR>", output["text"])
|
||||
if "text/plain" in output.get("data", {}):
|
||||
output["data"]["text/plain"] = re.sub(addr_pat, "<HEXADDR>", output["data"]["text/plain"])
|
||||
for key, value in output.get("data", {}).items():
|
||||
if isinstance(value, str):
|
||||
output["data"][key] = value
|
||||
|
||||
return output
|
||||
|
||||
|
||||
def assert_notebooks_equal(expected, actual):
|
||||
expected_cells = expected["cells"]
|
||||
actual_cells = actual["cells"]
|
||||
assert len(expected_cells) == len(actual_cells)
|
||||
|
||||
for expected_cell, actual_cell in zip(expected_cells, actual_cells):
|
||||
expected_outputs = expected_cell.get("outputs", [])
|
||||
actual_outputs = actual_cell.get("outputs", [])
|
||||
normalized_expected_outputs = list(map(normalize_output, expected_outputs))
|
||||
normalized_actual_outputs = list(map(normalize_output, actual_outputs))
|
||||
assert normalized_expected_outputs == normalized_actual_outputs
|
||||
|
||||
expected_execution_count = expected_cell.get("execution_count", None)
|
||||
actual_execution_count = actual_cell.get("execution_count", None)
|
||||
assert expected_execution_count == actual_execution_count
|
||||
|
||||
|
||||
def test_basic_execution():
|
||||
preprocessor = ExecutePreprocessor()
|
||||
fname = os.path.join(os.path.dirname(__file__), "files", "HelloWorld.ipynb")
|
||||
with open(fname) as f:
|
||||
input_nb = nbformat.read(f, 4)
|
||||
output_nb, _ = preprocessor.preprocess(deepcopy(input_nb))
|
||||
assert_notebooks_equal(input_nb, output_nb)
|
||||
|
||||
|
||||
def test_mixed_markdown_execution():
|
||||
preprocessor = ExecutePreprocessor()
|
||||
fname = os.path.join(os.path.dirname(__file__), "files", "MixedMarkdown.ipynb")
|
||||
with open(fname) as f:
|
||||
input_nb = nbformat.read(f, 4)
|
||||
output_nb, _ = preprocessor.preprocess(deepcopy(input_nb))
|
||||
assert_notebooks_equal(input_nb, output_nb)
|
||||
|
||||
|
||||
def test_executenb():
|
||||
fname = os.path.join(os.path.dirname(__file__), "files", "HelloWorld.ipynb")
|
||||
with open(fname) as f:
|
||||
input_nb = nbformat.read(f, 4)
|
||||
with pytest.warns(FutureWarning):
|
||||
output_nb = executenb(deepcopy(input_nb))
|
||||
assert_notebooks_equal(input_nb, output_nb)
|
||||
|
||||
|
||||
def test_populate_language_info():
|
||||
preprocessor = ExecutePreprocessor(kernel_name="python")
|
||||
nb = nbformat.v4.new_notebook() # Certainly has no language_info.
|
||||
preprocessor.preprocess(nb, resources={})
|
||||
# Should mutate input
|
||||
assert "language_info" in nb.metadata # See that a basic attribute is filled in
|
||||
|
||||
|
||||
def test_preprocess_cell():
|
||||
class CellReplacer(ExecutePreprocessor):
|
||||
def preprocess_cell(self, cell, resources, index, **kwargs):
|
||||
cell.source = "print('Ignored')"
|
||||
return super().preprocess_cell(cell, resources, index, **kwargs)
|
||||
|
||||
preprocessor = CellReplacer()
|
||||
fname = os.path.join(os.path.dirname(__file__), "files", "HelloWorld.ipynb")
|
||||
with open(fname) as f:
|
||||
input_nb = nbformat.read(f, 4)
|
||||
output_nb, _ = preprocessor.preprocess(deepcopy(input_nb))
|
||||
expected_nb = deepcopy(input_nb)
|
||||
for cell in expected_nb.cells:
|
||||
cell.source = "print('Ignored')"
|
||||
for output in cell.outputs:
|
||||
output.text = "Ignored\n"
|
||||
assert_notebooks_equal(expected_nb, output_nb)
|
@ -0,0 +1,85 @@
|
||||
"""Tests for the extractoutput preprocessor"""
|
||||
|
||||
# Copyright (c) IPython Development Team.
|
||||
# Distributed under the terms of the Modified BSD License.
|
||||
|
||||
import json
|
||||
|
||||
from ..extractoutput import ExtractOutputPreprocessor
|
||||
from .base import PreprocessorTestsBase
|
||||
|
||||
|
||||
class TestExtractOutput(PreprocessorTestsBase):
|
||||
"""Contains test functions for extractoutput.py"""
|
||||
|
||||
def build_preprocessor(self):
|
||||
"""Make an instance of a preprocessor"""
|
||||
preprocessor = ExtractOutputPreprocessor()
|
||||
preprocessor.extract_output_types = {"text/plain", "image/png", "application/pdf"}
|
||||
preprocessor.enabled = True
|
||||
return preprocessor
|
||||
|
||||
def test_constructor(self):
|
||||
"""Can a ExtractOutputPreprocessor be constructed?"""
|
||||
self.build_preprocessor()
|
||||
|
||||
def test_output(self):
|
||||
"""Test the output of the ExtractOutputPreprocessor"""
|
||||
nb = self.build_notebook()
|
||||
res = self.build_resources()
|
||||
preprocessor = self.build_preprocessor()
|
||||
nb, res = preprocessor(nb, res)
|
||||
# Check if text was extracted.
|
||||
output = nb.cells[0].outputs[1]
|
||||
self.assertIn("filenames", output.metadata)
|
||||
self.assertIn("text/plain", output.metadata.filenames)
|
||||
text_filename = output.metadata.filenames["text/plain"]
|
||||
|
||||
# Check if png was extracted.
|
||||
output = nb.cells[0].outputs[6]
|
||||
self.assertIn("filenames", output.metadata)
|
||||
self.assertIn("image/png", output.metadata.filenames)
|
||||
png_filename = output.metadata.filenames["image/png"]
|
||||
|
||||
# Check that pdf was extracted
|
||||
output = nb.cells[0].outputs[7]
|
||||
self.assertIn("filenames", output.metadata)
|
||||
self.assertIn("application/pdf", output.metadata.filenames)
|
||||
pdf_filename = output.metadata.filenames["application/pdf"]
|
||||
|
||||
# Verify text output
|
||||
self.assertIn(text_filename, res["outputs"])
|
||||
self.assertEqual(res["outputs"][text_filename], b"b")
|
||||
|
||||
# Verify png output
|
||||
self.assertIn(png_filename, res["outputs"])
|
||||
self.assertEqual(res["outputs"][png_filename], b"g")
|
||||
|
||||
# Verify pdf output
|
||||
self.assertIn(pdf_filename, res["outputs"])
|
||||
self.assertEqual(res["outputs"][pdf_filename], b"h")
|
||||
|
||||
def test_json_extraction(self):
|
||||
nb = self.build_notebook(with_json_outputs=True)
|
||||
res = self.build_resources()
|
||||
preprocessor = self.build_preprocessor()
|
||||
preprocessor.extract_output_types = {"application/json"}
|
||||
nb, res = preprocessor(nb, res)
|
||||
reference = self.build_notebook(with_json_outputs=True).cells[0].outputs
|
||||
|
||||
# Verify cell untouched
|
||||
self.assertEqual(
|
||||
[out.get("data") for out in nb.cells[0].outputs], [out.get("data") for out in reference]
|
||||
)
|
||||
|
||||
outputs = sorted(res["outputs"].values())
|
||||
reference_files = []
|
||||
for out in reference:
|
||||
try:
|
||||
data = out["data"]["application/json"]
|
||||
reference_files.append(json.dumps(data).encode())
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
# Verify equivalence of extracted outputs.
|
||||
self.assertEqual(sorted(outputs), sorted(reference_files))
|
@ -0,0 +1,53 @@
|
||||
"""Tests for the HighlightMagics preprocessor"""
|
||||
|
||||
from ..highlightmagics import HighlightMagicsPreprocessor
|
||||
from .base import PreprocessorTestsBase
|
||||
|
||||
|
||||
class TestHighlightMagics(PreprocessorTestsBase):
|
||||
"""Contains test functions for highlightmagics.py"""
|
||||
|
||||
def build_preprocessor(self):
|
||||
"""Make an instance of a preprocessor"""
|
||||
preprocessor = HighlightMagicsPreprocessor()
|
||||
preprocessor.enabled = True
|
||||
return preprocessor
|
||||
|
||||
def test_constructor(self):
|
||||
"""Can a HighlightMagicsPreprocessor be constructed?"""
|
||||
self.build_preprocessor()
|
||||
|
||||
def test_tagging(self):
|
||||
"""Test the HighlightMagicsPreprocessor tagging"""
|
||||
nb = self.build_notebook()
|
||||
res = self.build_resources()
|
||||
preprocessor = self.build_preprocessor()
|
||||
nb.cells[
|
||||
0
|
||||
].source = """%%R -i x,y -o XYcoef
|
||||
lm.fit <- lm(y~x)
|
||||
par(mfrow=c(2,2))
|
||||
print(summary(lm.fit))
|
||||
plot(lm.fit)
|
||||
XYcoef <- coef(lm.fit)"""
|
||||
|
||||
nb, res = preprocessor(nb, res)
|
||||
|
||||
assert "magics_language" in nb.cells[0]["metadata"]
|
||||
|
||||
self.assertEqual(nb.cells[0]["metadata"]["magics_language"], "r")
|
||||
|
||||
def test_no_false_positive(self):
|
||||
"""Test that HighlightMagicsPreprocessor does not tag false positives"""
|
||||
nb = self.build_notebook()
|
||||
res = self.build_resources()
|
||||
preprocessor = self.build_preprocessor()
|
||||
nb.cells[
|
||||
0
|
||||
].source = """# this should not be detected
|
||||
print(\"""
|
||||
%%R -i x, y
|
||||
\""")"""
|
||||
nb, res = preprocessor(nb, res)
|
||||
|
||||
assert "magics_language" not in nb.cells[0]["metadata"]
|
@ -0,0 +1,53 @@
|
||||
"""Tests for the latex preprocessor"""
|
||||
|
||||
# Copyright (c) IPython Development Team.
|
||||
# Distributed under the terms of the Modified BSD License.
|
||||
|
||||
from ..latex import LatexPreprocessor
|
||||
from .base import PreprocessorTestsBase
|
||||
|
||||
|
||||
class TestLatex(PreprocessorTestsBase):
|
||||
"""Contains test functions for latex.py"""
|
||||
|
||||
def build_preprocessor(self):
|
||||
"""Make an instance of a preprocessor"""
|
||||
preprocessor = LatexPreprocessor()
|
||||
preprocessor.enabled = True
|
||||
return preprocessor
|
||||
|
||||
def test_constructor(self):
|
||||
"""Can a LatexPreprocessor be constructed?"""
|
||||
self.build_preprocessor()
|
||||
|
||||
def test_output(self):
|
||||
"""Test the output of the LatexPreprocessor"""
|
||||
nb = self.build_notebook()
|
||||
res = self.build_resources()
|
||||
preprocessor = self.build_preprocessor()
|
||||
nb, res = preprocessor(nb, res)
|
||||
|
||||
# Make sure the code cell wasn't modified.
|
||||
self.assertEqual(nb.cells[0].source, "$ e $")
|
||||
|
||||
# Verify that the markdown cell wasn't processed.
|
||||
self.assertEqual(nb.cells[1].source, "$ e $")
|
||||
|
||||
def test_highlight(self):
|
||||
"""Check that highlighting style can be changed"""
|
||||
nb = self.build_notebook()
|
||||
res = self.build_resources()
|
||||
preprocessor = self.build_preprocessor()
|
||||
|
||||
# Set the style to a known builtin that's not the default
|
||||
preprocessor.style = "colorful"
|
||||
nb, res = preprocessor(nb, res)
|
||||
style_defs = res["latex"]["pygments_definitions"]
|
||||
|
||||
# Get the default
|
||||
from pygments.formatters import LatexFormatter
|
||||
|
||||
default_defs = LatexFormatter(style="default").get_style_defs()
|
||||
|
||||
# Verify that the style was in fact changed
|
||||
assert style_defs != default_defs
|
@ -0,0 +1,73 @@
|
||||
"""
|
||||
Module with tests for the RegexRemovePreprocessor.
|
||||
"""
|
||||
|
||||
# Copyright (c) IPython Development Team.
|
||||
# Distributed under the terms of the Modified BSD License.
|
||||
|
||||
import re
|
||||
|
||||
from nbformat import v4 as nbformat
|
||||
|
||||
from ..regexremove import RegexRemovePreprocessor
|
||||
from .base import PreprocessorTestsBase
|
||||
|
||||
|
||||
class TestRegexRemove(PreprocessorTestsBase):
|
||||
"""Contains test functions for regexremove.py"""
|
||||
|
||||
def build_notebook(self):
|
||||
notebook = super().build_notebook()
|
||||
# Add a few empty cells
|
||||
notebook.cells.extend(
|
||||
[
|
||||
nbformat.new_code_cell(""),
|
||||
nbformat.new_markdown_cell(" "),
|
||||
nbformat.new_raw_cell("\n"),
|
||||
nbformat.new_raw_cell("\t"),
|
||||
]
|
||||
)
|
||||
|
||||
return notebook
|
||||
|
||||
def build_preprocessor(self):
|
||||
"""Make an instance of a preprocessor"""
|
||||
preprocessor = RegexRemovePreprocessor()
|
||||
preprocessor.enabled = True
|
||||
return preprocessor
|
||||
|
||||
def test_constructor(self):
|
||||
"""Can a RegexRemovePreprocessor be constructed?"""
|
||||
self.build_preprocessor()
|
||||
|
||||
def test_output(self):
|
||||
"""Test the output of the RegexRemovePreprocessor"""
|
||||
pattern_lookup = {
|
||||
"disallow_whitespace": [r"\s*\Z"],
|
||||
"disallow_tab_newline": [r"\t\Z", r"\n\Z"],
|
||||
}
|
||||
expected_cell_count = {
|
||||
"default": 6, # nothing is removed
|
||||
"disallow_whitespace": 2, # all "empty" cells are removed
|
||||
"disallow_tab_newline": 4, # cells with tab and newline are removed
|
||||
"none": 6,
|
||||
}
|
||||
for method in ["default", "disallow_whitespace", "disallow_tab_newline", "none"]:
|
||||
nb = self.build_notebook()
|
||||
res = self.build_resources()
|
||||
|
||||
# Build the preprocessor and extend the list of patterns or use an empty list
|
||||
preprocessor = self.build_preprocessor()
|
||||
if method == "none":
|
||||
preprocessor.patterns = []
|
||||
else:
|
||||
preprocessor.patterns.extend(pattern_lookup.get(method, []))
|
||||
nb, res = preprocessor(nb, res)
|
||||
|
||||
self.assertEqual(len(nb.cells), expected_cell_count[method])
|
||||
|
||||
# Make sure none of the cells match the pattern
|
||||
patterns = list(map(re.compile, preprocessor.patterns))
|
||||
for cell in nb.cells:
|
||||
for pattern in patterns:
|
||||
self.assertFalse(pattern.match(cell.source))
|
@ -0,0 +1,182 @@
|
||||
"""Tests for the HTMLSanitize preprocessor"""
|
||||
|
||||
from nbformat import v4 as nbformat
|
||||
|
||||
from ..sanitize import SanitizeHTML
|
||||
from .base import PreprocessorTestsBase
|
||||
|
||||
|
||||
class TestSanitizer(PreprocessorTestsBase):
|
||||
"""Contains test functions for sanitize.py"""
|
||||
|
||||
maxDiff = None
|
||||
|
||||
def build_preprocessor(self):
|
||||
"""Make an instance of a preprocessor"""
|
||||
preprocessor = SanitizeHTML()
|
||||
preprocessor.enabled = True
|
||||
return preprocessor
|
||||
|
||||
def preprocess_source(self, cell_type, source, preprocessor):
|
||||
nb = self.build_notebook()
|
||||
res = self.build_resources()
|
||||
|
||||
nb.cells[0].cell_type = cell_type
|
||||
nb.cells[0].source = source
|
||||
|
||||
nb, res = preprocessor(nb, res)
|
||||
|
||||
return nb.cells[0].source
|
||||
|
||||
def test_constructor(self):
|
||||
"""Can a SanitizeHTML be constructed?"""
|
||||
self.build_preprocessor()
|
||||
|
||||
def test_svg_handling(self):
|
||||
"""
|
||||
Test to make sure that svgs are handled 'properly'
|
||||
|
||||
We only allow <img> tags (via markdown syntax) and not all the other ways
|
||||
to embed svg: <object>, <embed>, <iframe> nor inline <svg>
|
||||
"""
|
||||
preprocessor = self.build_preprocessor()
|
||||
preprocessor.strip = True
|
||||
|
||||
self.assertEqual(
|
||||
self.preprocess_source(
|
||||
"markdown",
|
||||
"""
|
||||

|
||||
|
||||
<object data="something.svg" type="image/svg+xml"></object>
|
||||
|
||||
<embed data="something.svg" type="image/svg+xml" />
|
||||
|
||||
<iframe src="http://example.com/something.svg"></iframe>
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 68 65">
|
||||
<path fill="#1A374D" d="M42 27v-20c0-3.7-3.3-7-7-7s-7 3.3-7 7v21l12 15-7 15.7c14.5 13.9 35 2.8 35-13.7 0-13.3-13.4-21.8-26-18zm6 25c-3.9 0-7-3.1-7-7s3.1-7 7-7 7 3.1 7 7-3.1 7-7 7z"/>
|
||||
<path d="M14 27v-20c0-3.7-3.3-7-7-7s-7 3.3-7 7v41c0 8.2 9.2 17 20 17s20-9.2 20-20c0-13.3-13.4-21.8-26-18zm6 25c-3.9 0-7-3.1-7-7s3.1-7 7-7 7 3.1 7 7-3.1 7-7 7z"/>
|
||||
</svg>
|
||||
""",
|
||||
preprocessor,
|
||||
).strip(),
|
||||
"""
|
||||

|
||||
""".strip(),
|
||||
)
|
||||
|
||||
def test_tag_allowlist_stripping(self):
|
||||
"""Test tag allowlisting + stripping out offending tags"""
|
||||
preprocessor = self.build_preprocessor()
|
||||
preprocessor.strip = True
|
||||
|
||||
self.assertEqual(
|
||||
self.preprocess_source(
|
||||
"markdown", "_A_ <em>few</em> <script>tags</script>", preprocessor
|
||||
),
|
||||
"_A_ <em>few</em> tags",
|
||||
)
|
||||
|
||||
def test_comment_stripping(self):
|
||||
"""Test HTML comment stripping"""
|
||||
preprocessor = self.build_preprocessor()
|
||||
|
||||
self.assertEqual(
|
||||
self.preprocess_source("markdown", "_A_ <em>few</em> <!-- tags -->", preprocessor),
|
||||
"_A_ <em>few</em> ",
|
||||
)
|
||||
|
||||
preprocessor.strip_comments = False
|
||||
self.assertEqual(
|
||||
self.preprocess_source("markdown", "_A_ <em>few</em> <!-- tags -->", preprocessor),
|
||||
"_A_ <em>few</em> <!-- tags -->",
|
||||
)
|
||||
|
||||
def test_attributes_allowlist(self):
|
||||
"""Test style"""
|
||||
preprocessor = self.build_preprocessor()
|
||||
|
||||
preprocessor.attributes["a"] = ["href", "title"]
|
||||
|
||||
self.assertEqual(
|
||||
self.preprocess_source(
|
||||
"markdown", '<a href="link" rel="nofollow">Hi</a>', preprocessor
|
||||
),
|
||||
'<a href="link">Hi</a>',
|
||||
)
|
||||
|
||||
def test_style_allowlist(self):
|
||||
"""Test style"""
|
||||
preprocessor = self.build_preprocessor()
|
||||
|
||||
if "*" in preprocessor.attributes:
|
||||
preprocessor.attributes["*"].append("style")
|
||||
else:
|
||||
preprocessor.attributes["*"] = ["style"]
|
||||
preprocessor.styles = [
|
||||
"color",
|
||||
]
|
||||
|
||||
self.assertEqual(
|
||||
self.preprocess_source(
|
||||
"markdown",
|
||||
'_A_ <em style="color: blue; background-color: pink">'
|
||||
"few</em> <script>tags</script>",
|
||||
preprocessor,
|
||||
),
|
||||
'_A_ <em style="color: blue;">few</em> <script>tags</script>',
|
||||
)
|
||||
|
||||
def test_tag_passthrough(self):
|
||||
"""Test passing through raw output"""
|
||||
preprocessor = self.build_preprocessor()
|
||||
|
||||
self.assertEqual(
|
||||
self.preprocess_source("raw", "_A_ <em>few</em> <script>tags</script>", preprocessor),
|
||||
"_A_ <em>few</em> <script>tags</script>",
|
||||
)
|
||||
|
||||
def test_output_sanitizing(self):
|
||||
"""Test that outputs are also sanitized properly"""
|
||||
preprocessor = self.build_preprocessor()
|
||||
nb = self.build_notebook()
|
||||
|
||||
outputs = [
|
||||
nbformat.new_output(
|
||||
"display_data",
|
||||
data={
|
||||
"text/plain": "b",
|
||||
"text/html": "<script>more evil</script>",
|
||||
"text/css": "<style> * {display:none}</style>",
|
||||
},
|
||||
),
|
||||
nbformat.new_output("stream", name="stdout", text="wat"),
|
||||
nbformat.new_output("stream", name="stdout", text="<script>Evil tag</script>"),
|
||||
]
|
||||
nb.cells[0].outputs = outputs
|
||||
|
||||
res = self.build_resources()
|
||||
nb, res = preprocessor(nb, res)
|
||||
|
||||
expected_output = [
|
||||
{
|
||||
"data": {"text/html": "<script>more evil</script>", "text/plain": "b"},
|
||||
"metadata": {},
|
||||
"output_type": "display_data",
|
||||
},
|
||||
{"name": "stdout", "output_type": "stream", "text": "wat"},
|
||||
{"name": "stdout", "output_type": "stream", "text": "<script>Evil tag</script>"},
|
||||
]
|
||||
self.assertEqual(nb.cells[0].outputs, expected_output)
|
||||
|
||||
def test_tag_allowlist(self):
|
||||
"""Test tag allowlisting"""
|
||||
preprocessor = self.build_preprocessor()
|
||||
|
||||
self.assertEqual(
|
||||
self.preprocess_source(
|
||||
"markdown", "_A_ <em>few</em> <script>tags</script>", preprocessor
|
||||
),
|
||||
"_A_ <em>few</em> <script>tags</script>",
|
||||
)
|
@ -0,0 +1,100 @@
|
||||
"""Tests for the svg2pdf preprocessor"""
|
||||
|
||||
# Copyright (c) IPython Development Team.
|
||||
# Distributed under the terms of the Modified BSD License.
|
||||
|
||||
from unittest.mock import patch
|
||||
|
||||
from nbformat import v4 as nbformat
|
||||
|
||||
from ...tests.utils import onlyif_cmds_exist
|
||||
from ..svg2pdf import SVG2PDFPreprocessor
|
||||
from .base import PreprocessorTestsBase
|
||||
|
||||
|
||||
class Testsvg2pdf(PreprocessorTestsBase):
|
||||
"""Contains test functions for svg2pdf.py"""
|
||||
|
||||
simple_svg = """<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
<svg
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
version="1.0"
|
||||
x="0.00000000"
|
||||
y="0.00000000"
|
||||
width="500.00000"
|
||||
height="500.00000"
|
||||
id="svg2">
|
||||
<defs
|
||||
id="defs4" />
|
||||
<g
|
||||
id="layer1">
|
||||
<rect
|
||||
width="300.00000"
|
||||
height="300.00000"
|
||||
x="100.00000"
|
||||
y="100.00000"
|
||||
style="opacity:1.0000000;fill:none;fill-opacity:1.0000000;fill-rule:evenodd;stroke:#000000;stroke-width:8.0000000;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4.0000000;stroke-dasharray:none;stroke-dashoffset:0.00000000;stroke-opacity:1.0000000"
|
||||
id="rect5719" />
|
||||
</g>
|
||||
</svg>"""
|
||||
|
||||
def build_notebook(self):
|
||||
"""Build a reveal slides notebook in memory for use with tests.
|
||||
Overrides base in PreprocessorTestsBase"""
|
||||
|
||||
outputs = [
|
||||
nbformat.new_output(output_type="display_data", data={"image/svg+xml": self.simple_svg})
|
||||
]
|
||||
|
||||
cells = [nbformat.new_code_cell(source="", execution_count=1, outputs=outputs)]
|
||||
|
||||
return nbformat.new_notebook(cells=cells)
|
||||
|
||||
def build_preprocessor(self, **kwargs):
|
||||
"""Make an instance of a preprocessor"""
|
||||
preprocessor = SVG2PDFPreprocessor(**kwargs)
|
||||
preprocessor.enabled = True
|
||||
return preprocessor
|
||||
|
||||
def test_constructor(self):
|
||||
"""Can a SVG2PDFPreprocessor be constructed?"""
|
||||
self.build_preprocessor()
|
||||
|
||||
@onlyif_cmds_exist("inkscape")
|
||||
def test_output(self):
|
||||
"""Test the output of the SVG2PDFPreprocessor"""
|
||||
nb = self.build_notebook()
|
||||
res = self.build_resources()
|
||||
preprocessor = self.build_preprocessor()
|
||||
nb, res = preprocessor(nb, res)
|
||||
self.assertIn("application/pdf", nb.cells[0].outputs[0].data)
|
||||
|
||||
@onlyif_cmds_exist("inkscape")
|
||||
@patch("subprocess.Popen")
|
||||
def test_inkscape_version_default(self, mock_popen):
|
||||
mock_popen().communicate.return_value = (b"Inkscape 0.92.3 (2405546, 2018-03-11)", b"")
|
||||
mock_popen().returncode = 0
|
||||
|
||||
preprocessor = self.build_preprocessor()
|
||||
assert preprocessor.inkscape_version == "0.92.3"
|
||||
|
||||
def test_inkscape_pre_v1_command(self):
|
||||
preprocessor = self.build_preprocessor(inkscape="fake-inkscape", inkscape_version="0.92.3")
|
||||
assert preprocessor.command == [
|
||||
"fake-inkscape",
|
||||
"--without-gui",
|
||||
"--export-pdf={to_filename}",
|
||||
"{from_filename}",
|
||||
]
|
||||
|
||||
def test_inkscape_v1_command(self):
|
||||
preprocessor = self.build_preprocessor(
|
||||
inkscape="fake-inkscape", inkscape_version="1.0beta2"
|
||||
)
|
||||
assert preprocessor.command == [
|
||||
"fake-inkscape",
|
||||
"--export-filename={to_filename}",
|
||||
"{from_filename}",
|
||||
]
|
@ -0,0 +1,88 @@
|
||||
"""
|
||||
Module with tests for the TagRemovePreprocessor.
|
||||
"""
|
||||
|
||||
# Copyright (c) IPython Development Team.
|
||||
# Distributed under the terms of the Modified BSD License.
|
||||
|
||||
from nbformat import v4 as nbformat
|
||||
|
||||
from ..tagremove import TagRemovePreprocessor
|
||||
from .base import PreprocessorTestsBase
|
||||
|
||||
|
||||
class TestTagRemove(PreprocessorTestsBase):
|
||||
"""Contains test functions for tagremove.py"""
|
||||
|
||||
def build_notebook(self):
|
||||
"""
|
||||
Build a notebook to have metadata tags for cells, output_areas, and
|
||||
individual outputs.
|
||||
"""
|
||||
notebook = super().build_notebook()
|
||||
# Add a few empty cells
|
||||
notebook.cells[0].outputs.extend(
|
||||
[
|
||||
nbformat.new_output(
|
||||
"display_data", data={"text/plain": "i"}, metadata={"tags": ["hide_one_output"]}
|
||||
),
|
||||
]
|
||||
)
|
||||
outputs_to_be_removed = [
|
||||
nbformat.new_output("display_data", data={"text/plain": "remove_my_output"}),
|
||||
]
|
||||
outputs_to_be_kept = [
|
||||
nbformat.new_output(
|
||||
"stream",
|
||||
name="stdout",
|
||||
text="remove_my_output",
|
||||
),
|
||||
]
|
||||
notebook.cells.extend(
|
||||
[
|
||||
nbformat.new_code_cell(
|
||||
source="display('remove_my_output')",
|
||||
execution_count=2,
|
||||
outputs=outputs_to_be_removed,
|
||||
metadata={"tags": ["hide_all_outputs"]},
|
||||
),
|
||||
nbformat.new_code_cell(
|
||||
source="print('remove this cell')",
|
||||
execution_count=3,
|
||||
outputs=outputs_to_be_kept,
|
||||
metadata={"tags": ["hide_this_cell"]},
|
||||
),
|
||||
]
|
||||
)
|
||||
|
||||
return notebook
|
||||
|
||||
def build_preprocessor(self):
|
||||
"""Make an instance of a preprocessor"""
|
||||
preprocessor = TagRemovePreprocessor()
|
||||
preprocessor.enabled = True
|
||||
return preprocessor
|
||||
|
||||
def test_constructor(self):
|
||||
"""Can a TagRemovePreprocessor be constructed?"""
|
||||
self.build_preprocessor()
|
||||
|
||||
def test_output(self):
|
||||
"""Test the output of the TagRemovePreprocessor"""
|
||||
nb = self.build_notebook()
|
||||
res = self.build_resources()
|
||||
preprocessor = self.build_preprocessor()
|
||||
preprocessor.remove_cell_tags.add("hide_this_cell")
|
||||
preprocessor.remove_all_outputs_tags.add("hide_all_outputs")
|
||||
preprocessor.remove_single_output_tags.add("hide_one_output")
|
||||
|
||||
nb, res = preprocessor(nb, res)
|
||||
|
||||
# checks that we can remove entire cells
|
||||
self.assertEqual(len(nb.cells), 3)
|
||||
|
||||
# checks that we can remove output areas
|
||||
self.assertEqual(len(nb.cells[-1].outputs), 0)
|
||||
|
||||
# checks that we can remove individual outputs
|
||||
self.assertEqual(len(nb.cells[0].outputs), 8)
|
Reference in New Issue
Block a user