Uploaded Test files

This commit is contained in:
Batuhan Berk Başoğlu 2020-11-12 11:05:57 -05:00
parent f584ad9d97
commit 2e81cb7d99
16627 changed files with 2065359 additions and 102444 deletions

View 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

View 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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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()

View 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

View 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

View file

@ -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

View 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

View file

@ -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

View 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,
)

View 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")

View 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', []))

View 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

View file

@ -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)

View file

@ -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
}

View file

@ -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
}

View file

@ -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' } }

View file

@ -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

View file

@ -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')

View file

@ -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']

View file

@ -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)

View file

@ -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))

View file

@ -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'])

View file

@ -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

View file

@ -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))

View file

@ -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',
"""
![some image](http://example.com/something.svg)
<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(),
"""
![some image](http://example.com/something.svg)
""".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> '
'&lt;script&gt;tags&lt;/script&gt;'
)
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> &lt;script&gt;tags&lt;/script&gt;'
)
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': '&lt;script&gt;more evil&lt;/script&gt;',
'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> &lt;script&gt;tags&lt;/script&gt;'
)

View file

@ -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}"'

View file

@ -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)