Uploaded Test files
This commit is contained in:
parent
f584ad9d97
commit
2e81cb7d99
16627 changed files with 2065359 additions and 102444 deletions
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
|
||||
from .base import Preprocessor
|
||||
from .convertfigures import ConvertFiguresPreprocessor
|
||||
from .svg2pdf import SVG2PDFPreprocessor
|
||||
from .extractoutput import ExtractOutputPreprocessor
|
||||
from .latex import LatexPreprocessor
|
||||
from .csshtmlheader import CSSHTMLHeaderPreprocessor
|
||||
from .highlightmagics import HighlightMagicsPreprocessor
|
||||
from .clearoutput import ClearOutputPreprocessor
|
||||
from .execute import ExecutePreprocessor
|
||||
from .regexremove import RegexRemovePreprocessor
|
||||
from .tagremove import TagRemovePreprocessor
|
||||
from .clearmetadata import ClearMetadataPreprocessor
|
||||
|
||||
# decorated function Preprocessors
|
||||
from .coalescestreams import coalesce_streams
|
||||
|
||||
# Backwards compatability for imported name
|
||||
from nbclient.exceptions import CellExecutionError
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
90
venv/Lib/site-packages/nbconvert/preprocessors/base.py
Normal file
90
venv/Lib/site-packages/nbconvert/preprocessors/base.py
Normal file
|
@ -0,0 +1,90 @@
|
|||
"""Base class for preprocessors"""
|
||||
|
||||
# Copyright (c) IPython Development Team.
|
||||
# Distributed under the terms of the Modified BSD License.
|
||||
|
||||
from ..utils.base import NbConvertBase
|
||||
from traitlets import Bool
|
||||
|
||||
|
||||
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
|
||||
|
|
@ -0,0 +1,85 @@
|
|||
"""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,30 @@
|
|||
"""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,76 @@
|
|||
"""Preprocessor for merging consecutive stream outputs for easier handling."""
|
||||
|
||||
# Copyright (c) IPython Development Team.
|
||||
# Distributed under the terms of the Modified BSD License.
|
||||
|
||||
import re
|
||||
import functools
|
||||
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,48 @@
|
|||
"""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 .base import Preprocessor
|
||||
from traitlets import Unicode
|
||||
|
||||
|
||||
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,91 @@
|
|||
"""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 os
|
||||
import io
|
||||
import hashlib
|
||||
import nbconvert.resources
|
||||
|
||||
from traitlets import Unicode, Union, Type
|
||||
from pygments.style import Style
|
||||
from jupyterlab_pygments import JupyterStyle
|
||||
|
||||
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 io.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()
|
147
venv/Lib/site-packages/nbconvert/preprocessors/execute.py
Normal file
147
venv/Lib/site-packages/nbconvert/preprocessors/execute.py
Normal file
|
@ -0,0 +1,147 @@
|
|||
"""Module containing a preprocessor that executes the code cells
|
||||
and updates outputs"""
|
||||
|
||||
# Copyright (c) IPython Development Team.
|
||||
# Distributed under the terms of the Modified BSD License.
|
||||
from typing import Optional
|
||||
from nbformat import NotebookNode
|
||||
from nbclient import NotebookClient, execute as _execute
|
||||
from nbclient.util import run_sync
|
||||
# Backwards compatability for imported name
|
||||
from nbclient.exceptions import CellExecutionError
|
||||
|
||||
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 unfortunatley 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, 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 exeuction 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 nbcovnert 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._check_assign_resources(resources)
|
||||
self.execute()
|
||||
return self.nb, self.resources
|
||||
|
||||
async def async_execute_cell(
|
||||
self,
|
||||
cell: NotebookNode,
|
||||
cell_index: int,
|
||||
execution_count: Optional[int] = None,
|
||||
store_history: bool = False) -> NotebookNode:
|
||||
"""
|
||||
Executes a single code cell.
|
||||
|
||||
Overwrites NotebookClient's version of this method to allow for preprocess_cell calls.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
cell : nbformat.NotebookNode
|
||||
The cell which is currently being processed.
|
||||
cell_index : int
|
||||
The position of the cell within the notebook object.
|
||||
execution_count : int
|
||||
The execution count to be assigned to the cell (default: Use kernel response)
|
||||
store_history : bool
|
||||
Determines if history should be stored in the kernel (default: False).
|
||||
Specific to ipython kernels, which can store command histories.
|
||||
|
||||
Returns
|
||||
-------
|
||||
output : dict
|
||||
The execution output payload (or None for no output).
|
||||
|
||||
Raises
|
||||
------
|
||||
CellExecutionError
|
||||
If execution failed and should raise an exception, this will be raised
|
||||
with defaults about the failure.
|
||||
|
||||
Returns
|
||||
-------
|
||||
cell : NotebookNode
|
||||
The cell which was just processed.
|
||||
"""
|
||||
# Copied and intercepted to allow for custom preprocess_cell contracts to be fullfilled
|
||||
self.store_history = store_history
|
||||
cell, resources = self.preprocess_cell(cell, self.resources, cell_index)
|
||||
# Apply rules from nbclient for where to apply execution counts
|
||||
if execution_count and cell.cell_type == 'code' and cell.source.strip():
|
||||
cell['execution_count'] = execution_count
|
||||
return cell, resources
|
||||
|
||||
def preprocess_cell(self, cell, resources, index, **kwargs):
|
||||
"""
|
||||
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)
|
||||
# Because nbclient is an async library, we need to wrap the parent async call to generate a syncronous version.
|
||||
cell = run_sync(NotebookClient.async_execute_cell)(self, cell, index, store_history=self.store_history)
|
||||
return cell, self.resources
|
152
venv/Lib/site-packages/nbconvert/preprocessors/extractoutput.py
Normal file
152
venv/Lib/site-packages/nbconvert/preprocessors/extractoutput.py
Normal file
|
@ -0,0 +1,152 @@
|
|||
"""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.
|
||||
|
||||
from textwrap import dedent
|
||||
from binascii import a2b_base64
|
||||
import sys
|
||||
import os
|
||||
import json
|
||||
from mimetypes import guess_extension
|
||||
|
||||
from traitlets import Unicode, Set
|
||||
from .base import Preprocessor
|
||||
|
||||
if sys.version_info < (3,):
|
||||
text_type = basestring
|
||||
else:
|
||||
text_type = str
|
||||
|
||||
|
||||
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, text_type):
|
||||
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, text_type):
|
||||
# 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) and not isinstance(data, text_type):
|
||||
# In python 3 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,100 @@
|
|||
"""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.
|
||||
|
||||
from __future__ import print_function, absolute_import
|
||||
|
||||
import re
|
||||
|
||||
from .base import Preprocessor
|
||||
from traitlets import Dict
|
||||
|
||||
|
||||
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(
|
||||
r'^\s*({0})\s+'.format(any_language))
|
||||
|
||||
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
|
53
venv/Lib/site-packages/nbconvert/preprocessors/latex.py
Normal file
53
venv/Lib/site-packages/nbconvert/preprocessors/latex.py
Normal file
|
@ -0,0 +1,53 @@
|
|||
"""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 __future__ import print_function, absolute_import
|
||||
|
||||
from .base import Preprocessor
|
||||
from traitlets import Unicode
|
||||
|
||||
#-----------------------------------------------------------------------------
|
||||
# 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,69 @@
|
|||
"""
|
||||
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
|
138
venv/Lib/site-packages/nbconvert/preprocessors/sanitize.py
Normal file
138
venv/Lib/site-packages/nbconvert/preprocessors/sanitize.py
Normal file
|
@ -0,0 +1,138 @@
|
|||
"""
|
||||
NBConvert Preprocessor for sanitizing HTML rendering of notebooks.
|
||||
"""
|
||||
|
||||
from bleach import (
|
||||
ALLOWED_ATTRIBUTES,
|
||||
ALLOWED_STYLES,
|
||||
ALLOWED_TAGS,
|
||||
clean,
|
||||
)
|
||||
from traitlets import (
|
||||
Any,
|
||||
Bool,
|
||||
List,
|
||||
Set,
|
||||
Unicode,
|
||||
)
|
||||
from .base import Preprocessor
|
||||
|
||||
|
||||
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 whitelisted"
|
||||
)
|
||||
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.
|
||||
"""
|
||||
return clean(
|
||||
html_str,
|
||||
tags=self.tags,
|
||||
attributes=self.attributes,
|
||||
styles=self.styles,
|
||||
strip=self.strip,
|
||||
strip_comments=self.strip_comments,
|
||||
)
|
136
venv/Lib/site-packages/nbconvert/preprocessors/svg2pdf.py
Normal file
136
venv/Lib/site-packages/nbconvert/preprocessors/svg2pdf.py
Normal file
|
@ -0,0 +1,136 @@
|
|||
"""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 io
|
||||
import os
|
||||
import sys
|
||||
import subprocess
|
||||
|
||||
from testpath.tempdir import TemporaryDirectory
|
||||
from traitlets import Unicode, default
|
||||
|
||||
from .convertfigures import ConvertFiguresPreprocessor
|
||||
|
||||
from shutil import which
|
||||
|
||||
|
||||
# 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]
|
||||
|
||||
command = Unicode(
|
||||
help="""The command to use for converting SVG to PDF
|
||||
|
||||
This string 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}.
|
||||
""").tag(config=True)
|
||||
|
||||
@default('command')
|
||||
def _command_default(self):
|
||||
major_verison = self.inkscape_version.split('.')[0]
|
||||
export_option = ' --export-filename' if int(major_verison) > 0 else ' --export-pdf'
|
||||
gui_option = '' if int(major_verison) > 0 else ' --without-gui'
|
||||
|
||||
return '{inkscape}{gui_option}{export_option}='.format(
|
||||
inkscape=self.inkscape, export_option=export_option, gui_option=gui_option
|
||||
) + '"{to_filename}" "{from_filename}"'
|
||||
|
||||
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 io.open(input_filename, 'w', encoding='utf8') as f:
|
||||
f.write(data)
|
||||
|
||||
# Call conversion application
|
||||
output_filename = os.path.join(tmpdir, 'figure.pdf')
|
||||
shell = self.command.format(from_filename=input_filename,
|
||||
to_filename=output_filename)
|
||||
subprocess.call(shell, shell=True) # Shell=True okay since input is trusted.
|
||||
|
||||
# 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")
|
123
venv/Lib/site-packages/nbconvert/preprocessors/tagremove.py
Normal file
123
venv/Lib/site-packages/nbconvert/preprocessors/tagremove.py
Normal file
|
@ -0,0 +1,123 @@
|
|||
"""
|
||||
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', []))
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
55
venv/Lib/site-packages/nbconvert/preprocessors/tests/base.py
Normal file
55
venv/Lib/site-packages/nbconvert/preprocessors/tests/base.py
Normal file
|
@ -0,0 +1,55 @@
|
|||
"""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 ...tests.base import TestsBase
|
||||
from ...exporters.exporter import ResourcesDict
|
||||
|
||||
|
||||
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,28 @@
|
|||
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,133 @@
|
|||
"""
|
||||
Module with tests for the clearmetadata preprocessor.
|
||||
"""
|
||||
|
||||
# Copyright (c) IPython Development Team.
|
||||
# Distributed under the terms of the Modified BSD License.
|
||||
|
||||
from .base import PreprocessorTestsBase
|
||||
from ..clearmetadata import ClearMetadataPreprocessor
|
||||
|
||||
|
||||
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 .base import PreprocessorTestsBase
|
||||
from ..clearoutput import ClearOutputPreprocessor
|
||||
|
||||
|
||||
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,58 @@
|
|||
"""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 .base import PreprocessorTestsBase
|
||||
from ..coalescestreams import coalesce_streams
|
||||
|
||||
|
||||
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, u'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, u'a\nb\nc\nd')
|
|
@ -0,0 +1,47 @@
|
|||
"""
|
||||
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 .base import PreprocessorTestsBase
|
||||
from ..csshtmlheader import CSSHTMLHeaderPreprocessor
|
||||
|
||||
|
||||
#-----------------------------------------------------------------------------
|
||||
# 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,109 @@
|
|||
# coding=utf-8
|
||||
|
||||
"""
|
||||
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
|
||||
import pytest
|
||||
import nbformat
|
||||
|
||||
from copy import deepcopy
|
||||
|
||||
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,86 @@
|
|||
"""Tests for the extractoutput preprocessor"""
|
||||
|
||||
# Copyright (c) IPython Development Team.
|
||||
# Distributed under the terms of the Modified BSD License.
|
||||
|
||||
import json
|
||||
|
||||
from .base import PreprocessorTestsBase
|
||||
from ..extractoutput import ExtractOutputPreprocessor
|
||||
|
||||
|
||||
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,50 @@
|
|||
"""Tests for the HighlightMagics preprocessor"""
|
||||
|
||||
from .base import PreprocessorTestsBase
|
||||
from ..highlightmagics import HighlightMagicsPreprocessor
|
||||
|
||||
|
||||
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,54 @@
|
|||
"""Tests for the latex preprocessor"""
|
||||
|
||||
# Copyright (c) IPython Development Team.
|
||||
# Distributed under the terms of the Modified BSD License.
|
||||
|
||||
from .base import PreprocessorTestsBase
|
||||
from ..latex import LatexPreprocessor
|
||||
|
||||
|
||||
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,71 @@
|
|||
"""
|
||||
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_dict
|
||||
|
||||
from .base import PreprocessorTestsBase
|
||||
from ..regexremove import RegexRemovePreprocessor
|
||||
|
||||
|
||||
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,208 @@
|
|||
"""Tests for the HTMLSanitize preprocessor"""
|
||||
|
||||
from .base import PreprocessorTestsBase
|
||||
from ..sanitize import SanitizeHTML
|
||||
from nbformat import v4 as nbformat
|
||||
|
||||
|
||||
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_whitelist_stripping(self):
|
||||
"""Test tag whitelisting + 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_whitelist(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_whitelist(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_whitelist(self):
|
||||
"""Test tag whitelisting"""
|
||||
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,94 @@
|
|||
"""Tests for the svg2pdf preprocessor"""
|
||||
|
||||
# Copyright (c) IPython Development Team.
|
||||
# Distributed under the terms of the Modified BSD License.
|
||||
|
||||
from nbformat import v4 as nbformat
|
||||
from unittest.mock import patch, Mock
|
||||
|
||||
from .base import PreprocessorTestsBase
|
||||
from ..svg2pdf import SVG2PDFPreprocessor
|
||||
from ...tests.utils import onlyif_cmds_exist
|
||||
|
||||
|
||||
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)
|
||||
|
||||
@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_version='0.92.3')
|
||||
assert preprocessor.command == '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,83 @@
|
|||
"""
|
||||
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 .base import PreprocessorTestsBase
|
||||
from ..tagremove import TagRemovePreprocessor
|
||||
|
||||
|
||||
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)
|
Loading…
Add table
Add a link
Reference in a new issue