first commit

This commit is contained in:
Ayxan
2022-05-23 00:16:32 +04:00
commit d660f2a4ca
24786 changed files with 4428337 additions and 0 deletions

View File

@ -0,0 +1,52 @@
"""utility functions for preprocessor tests"""
# Copyright (c) IPython Development Team.
# Distributed under the terms of the Modified BSD License.
from nbformat import v4 as nbformat
from ...exporters.exporter import ResourcesDict
from ...tests.base import TestsBase
class PreprocessorTestsBase(TestsBase):
"""Contains test functions preprocessor tests"""
def build_notebook(self, with_json_outputs=False):
"""Build a notebook in memory for use with preprocessor tests"""
outputs = [
nbformat.new_output("stream", name="stdout", text="a"),
nbformat.new_output("display_data", data={"text/plain": "b"}),
nbformat.new_output("stream", name="stdout", text="c"),
nbformat.new_output("stream", name="stdout", text="d"),
nbformat.new_output("stream", name="stderr", text="e"),
nbformat.new_output("stream", name="stderr", text="f"),
nbformat.new_output("display_data", data={"image/png": "Zw=="}), # g
nbformat.new_output("display_data", data={"application/pdf": "aA=="}), # h
]
if with_json_outputs:
outputs.extend(
[
nbformat.new_output("display_data", data={"application/json": [1, 2, 3]}), # j
nbformat.new_output(
"display_data", data={"application/json": {"a": 1, "c": {"b": 2}}}
), # k
nbformat.new_output("display_data", data={"application/json": "abc"}), # l
nbformat.new_output("display_data", data={"application/json": 15.03}), # m
]
)
cells = [
nbformat.new_code_cell(source="$ e $", execution_count=1, outputs=outputs),
nbformat.new_markdown_cell(source="$ e $"),
]
return nbformat.new_notebook(cells=cells)
def build_resources(self):
"""Build an empty resources dictionary."""
res = ResourcesDict()
res["metadata"] = ResourcesDict()
return res

View File

@ -0,0 +1,24 @@
from jupyter_client.manager import KernelManager
class FakeCustomKernelManager(KernelManager):
expected_methods = {
"__init__": 0,
"client": 0,
"start_kernel": 0,
}
def __init__(self, *args, **kwargs):
self.log.info("FakeCustomKernelManager initialized")
self.expected_methods["__init__"] += 1
super().__init__(*args, **kwargs)
def start_kernel(self, *args, **kwargs):
self.log.info("FakeCustomKernelManager started a kernel")
self.expected_methods["start_kernel"] += 1
return super().start_kernel(*args, **kwargs)
def client(self, *args, **kwargs):
self.log.info("FakeCustomKernelManager created a client")
self.expected_methods["client"] += 1
return super().client(*args, **kwargs)

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,134 @@
"""
Module with tests for the clearmetadata preprocessor.
"""
# Copyright (c) IPython Development Team.
# Distributed under the terms of the Modified BSD License.
from ..clearmetadata import ClearMetadataPreprocessor
from .base import PreprocessorTestsBase
class TestClearMetadata(PreprocessorTestsBase):
"""Contains test functions for clearmetadata.py"""
def build_notebook(self):
notebook = super().build_notebook()
notebook.metadata = {
"language_info": {"name": "python", "version": "3.6.7"},
"kernelspec": {"language": "python", "name": "python3"},
}
# Add a test field to the first cell
if "metadata" not in notebook.cells[0]:
notebook.cells[0].metadata = {}
notebook.cells[0].metadata["test_field"] = "test_value"
notebook.cells[0].metadata["test_nested"] = {"test_keep": "keep", "test_filtered": "filter"}
notebook.cells[0].metadata["executeTime"] = dict(
[("end_time", "09:31:50"), ("start_time", "09:31:49")]
)
return notebook
def build_preprocessor(self, **kwargs):
"""Make an instance of a preprocessor"""
preprocessor = ClearMetadataPreprocessor(**kwargs)
preprocessor.enabled = True
return preprocessor
def test_constructor(self):
"""Can a ClearMetadataPreprocessor be constructed?"""
self.build_preprocessor()
def test_default_output(self):
"""Test the output of the ClearMetadataPreprocessor"""
nb = self.build_notebook()
res = self.build_resources()
preprocessor = self.build_preprocessor()
nb, res = preprocessor(nb, res)
assert not nb.cells[0].metadata
# By default we only perserve the langauge name
assert nb.metadata == {"language_info": {"name": "python"}}
def test_cell_only(self):
"""Test the output of the ClearMetadataPreprocessor"""
nb = self.build_notebook()
res = self.build_resources()
preprocessor = self.build_preprocessor(clear_notebook_metadata=False)
nb, res = preprocessor(nb, res)
assert not nb.cells[0].metadata
assert nb.metadata
def test_notebook_only(self):
"""Test the output of the ClearMetadataPreprocessor"""
nb = self.build_notebook()
res = self.build_resources()
preprocessor = self.build_preprocessor(
clear_cell_metadata=False, preserve_nb_metadata_mask=set()
)
nb, res = preprocessor(nb, res)
assert nb.cells[0].metadata
assert not nb.metadata
def test_selective_cell_metadata(self):
"""Test the output of the ClearMetadataPreprocessor"""
nb = self.build_notebook()
res = self.build_resources()
preprocessor = self.build_preprocessor(
preserve_cell_metadata_mask=["test_field"], preserve_nb_metadata_mask=set()
)
nb, res = preprocessor(nb, res)
assert nb.cells[0].metadata == {"test_field": "test_value"}
assert not nb.metadata
def test_selective_cell_tuple_metadata(self):
"""Test the output of the ClearMetadataPreprocessor"""
nb = self.build_notebook()
res = self.build_resources()
# Ensure that a tuple length 1 works as well as a string key
preprocessor = self.build_preprocessor(
preserve_cell_metadata_mask=[("test_field",)], preserve_nb_metadata_mask=set()
)
nb, res = preprocessor(nb, res)
assert nb.cells[0].metadata == {"test_field": "test_value"}
assert not nb.metadata
def test_nested_cell_metadata(self):
"""Test the output of the ClearMetadataPreprocessor"""
nb = self.build_notebook()
res = self.build_resources()
preprocessor = self.build_preprocessor(
preserve_cell_metadata_mask=[("test_nested", "test_keep")],
preserve_nb_metadata_mask=set(),
)
nb, res = preprocessor(nb, res)
assert nb.cells[0].metadata == {"test_nested": {"test_keep": "keep"}}
assert not nb.metadata
def test_nested_cell_tuple_metadata(self):
"""Test the output of the ClearMetadataPreprocessor"""
nb = self.build_notebook()
res = self.build_resources()
# Ensure that a tuple length 1 works as well as a string key
preprocessor = self.build_preprocessor(
preserve_cell_metadata_mask=[("test_nested", ("test_keep",))],
preserve_nb_metadata_mask=set(),
)
nb, res = preprocessor(nb, res)
assert nb.cells[0].metadata == {"test_nested": {"test_keep": "keep"}}
assert not nb.metadata
def test_selective_notebook_metadata(self):
"""Test the output of the ClearMetadataPreprocessor"""
nb = self.build_notebook()
res = self.build_resources()
preprocessor = self.build_preprocessor(preserve_nb_metadata_mask=["kernelspec"])
nb, res = preprocessor(nb, res)
assert not nb.cells[0].metadata
assert nb.metadata == {"kernelspec": {"language": "python", "name": "python3"}}

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 ..clearoutput import ClearOutputPreprocessor
from .base import PreprocessorTestsBase
class TestClearOutput(PreprocessorTestsBase):
"""Contains test functions for clearoutput.py"""
def build_notebook(self):
notebook = super().build_notebook()
# Add a test field to the first cell
if "metadata" not in notebook.cells[0]:
notebook.cells[0].metadata = {}
notebook.cells[0].metadata["test_field"] = "test_value"
return notebook
def build_preprocessor(self):
"""Make an instance of a preprocessor"""
preprocessor = ClearOutputPreprocessor()
preprocessor.enabled = True
return preprocessor
def test_constructor(self):
"""Can a ClearOutputPreprocessor be constructed?"""
self.build_preprocessor()
def test_output(self):
"""Test the output of the ClearOutputPreprocessor"""
for remove_test_field in [False, True]:
nb = self.build_notebook()
res = self.build_resources()
preprocessor = self.build_preprocessor()
# Also remove the test field in addition to defaults
if remove_test_field:
preprocessor.remove_metadata_fields.add("test_field")
nb, res = preprocessor(nb, res)
assert nb.cells[0].outputs == []
assert nb.cells[0].execution_count is None
if "metadata" in nb.cells[0]:
for field in preprocessor.remove_metadata_fields:
assert field not in nb.cells[0].metadata
# Ensure the test field is only removed when added to the traitlet
assert remove_test_field or "test_field" in nb.cells[0].metadata

View File

@ -0,0 +1,62 @@
"""Tests for the coalescestreams preprocessor"""
# Copyright (c) IPython Development Team.
# Distributed under the terms of the Modified BSD License.
from nbformat import v4 as nbformat
from ..coalescestreams import coalesce_streams
from .base import PreprocessorTestsBase
class TestCoalesceStreams(PreprocessorTestsBase):
"""Contains test functions for coalescestreams.py"""
def test_coalesce_streams(self):
"""coalesce_streams preprocessor output test"""
nb = self.build_notebook()
res = self.build_resources()
nb, res = coalesce_streams(nb, res)
outputs = nb.cells[0].outputs
self.assertEqual(outputs[0].text, "a")
self.assertEqual(outputs[1].output_type, "display_data")
self.assertEqual(outputs[2].text, "cd")
self.assertEqual(outputs[3].text, "ef")
def test_coalesce_sequenced_streams(self):
"""Can the coalesce streams preprocessor merge a sequence of streams?"""
outputs = [
nbformat.new_output(output_type="stream", name="stdout", text="0"),
nbformat.new_output(output_type="stream", name="stdout", text="1"),
nbformat.new_output(output_type="stream", name="stdout", text="2"),
nbformat.new_output(output_type="stream", name="stdout", text="3"),
nbformat.new_output(output_type="stream", name="stdout", text="4"),
nbformat.new_output(output_type="stream", name="stdout", text="5"),
nbformat.new_output(output_type="stream", name="stdout", text="6"),
nbformat.new_output(output_type="stream", name="stdout", text="7"),
]
cells = [nbformat.new_code_cell(source="# None", execution_count=1, outputs=outputs)]
nb = nbformat.new_notebook(cells=cells)
res = self.build_resources()
nb, res = coalesce_streams(nb, res)
outputs = nb.cells[0].outputs
self.assertEqual(outputs[0].text, "01234567")
def test_coalesce_replace_streams(self):
"""Are \\r characters handled?"""
outputs = [
nbformat.new_output(output_type="stream", name="stdout", text="z"),
nbformat.new_output(output_type="stream", name="stdout", text="\ra"),
nbformat.new_output(output_type="stream", name="stdout", text="\nz\rb"),
nbformat.new_output(output_type="stream", name="stdout", text="\nz"),
nbformat.new_output(output_type="stream", name="stdout", text="\rc\n"),
nbformat.new_output(output_type="stream", name="stdout", text="z\rz\rd"),
]
cells = [nbformat.new_code_cell(source="# None", execution_count=1, outputs=outputs)]
nb = nbformat.new_notebook(cells=cells)
res = self.build_resources()
nb, res = coalesce_streams(nb, res)
outputs = nb.cells[0].outputs
self.assertEqual(outputs[0].text, "a\nb\nc\nd")

View File

@ -0,0 +1,44 @@
"""
Module with tests for the csshtmlheader preprocessor
"""
# -----------------------------------------------------------------------------
# Copyright (c) 2013, the IPython Development Team.
#
# Distributed under the terms of the Modified BSD License.
#
# The full license is in the file COPYING.txt, distributed with this software.
# -----------------------------------------------------------------------------
# -----------------------------------------------------------------------------
# Imports
# -----------------------------------------------------------------------------
from ..csshtmlheader import CSSHTMLHeaderPreprocessor
from .base import PreprocessorTestsBase
# -----------------------------------------------------------------------------
# Class
# -----------------------------------------------------------------------------
class TestCSSHTMLHeader(PreprocessorTestsBase):
"""Contains test functions for csshtmlheader.py"""
def build_preprocessor(self):
"""Make an instance of a preprocessor"""
preprocessor = CSSHTMLHeaderPreprocessor()
preprocessor.enabled = True
return preprocessor
def test_constructor(self):
"""Can a CSSHTMLHeaderPreprocessor be constructed?"""
self.build_preprocessor()
def test_output(self):
"""Test the output of the CSSHTMLHeaderPreprocessor"""
nb = self.build_notebook()
res = self.build_resources()
preprocessor = self.build_preprocessor()
nb, res = preprocessor(nb, res)
assert "css" in res["inlining"]

View File

@ -0,0 +1,105 @@
"""
Module with tests for the execute preprocessor.
"""
# Copyright (c) IPython Development Team.
# Distributed under the terms of the Modified BSD License.
import os
import re
from copy import deepcopy
import nbformat
import pytest
from ..execute import ExecutePreprocessor, executenb
addr_pat = re.compile(r"0x[0-9a-f]{7,9}")
def normalize_output(output):
"""
Normalizes (most) outputs for comparison.
"""
output = dict(output)
if "metadata" in output:
del output["metadata"]
if "text" in output:
output["text"] = re.sub(addr_pat, "<HEXADDR>", output["text"])
if "text/plain" in output.get("data", {}):
output["data"]["text/plain"] = re.sub(addr_pat, "<HEXADDR>", output["data"]["text/plain"])
for key, value in output.get("data", {}).items():
if isinstance(value, str):
output["data"][key] = value
return output
def assert_notebooks_equal(expected, actual):
expected_cells = expected["cells"]
actual_cells = actual["cells"]
assert len(expected_cells) == len(actual_cells)
for expected_cell, actual_cell in zip(expected_cells, actual_cells):
expected_outputs = expected_cell.get("outputs", [])
actual_outputs = actual_cell.get("outputs", [])
normalized_expected_outputs = list(map(normalize_output, expected_outputs))
normalized_actual_outputs = list(map(normalize_output, actual_outputs))
assert normalized_expected_outputs == normalized_actual_outputs
expected_execution_count = expected_cell.get("execution_count", None)
actual_execution_count = actual_cell.get("execution_count", None)
assert expected_execution_count == actual_execution_count
def test_basic_execution():
preprocessor = ExecutePreprocessor()
fname = os.path.join(os.path.dirname(__file__), "files", "HelloWorld.ipynb")
with open(fname) as f:
input_nb = nbformat.read(f, 4)
output_nb, _ = preprocessor.preprocess(deepcopy(input_nb))
assert_notebooks_equal(input_nb, output_nb)
def test_mixed_markdown_execution():
preprocessor = ExecutePreprocessor()
fname = os.path.join(os.path.dirname(__file__), "files", "MixedMarkdown.ipynb")
with open(fname) as f:
input_nb = nbformat.read(f, 4)
output_nb, _ = preprocessor.preprocess(deepcopy(input_nb))
assert_notebooks_equal(input_nb, output_nb)
def test_executenb():
fname = os.path.join(os.path.dirname(__file__), "files", "HelloWorld.ipynb")
with open(fname) as f:
input_nb = nbformat.read(f, 4)
with pytest.warns(FutureWarning):
output_nb = executenb(deepcopy(input_nb))
assert_notebooks_equal(input_nb, output_nb)
def test_populate_language_info():
preprocessor = ExecutePreprocessor(kernel_name="python")
nb = nbformat.v4.new_notebook() # Certainly has no language_info.
preprocessor.preprocess(nb, resources={})
# Should mutate input
assert "language_info" in nb.metadata # See that a basic attribute is filled in
def test_preprocess_cell():
class CellReplacer(ExecutePreprocessor):
def preprocess_cell(self, cell, resources, index, **kwargs):
cell.source = "print('Ignored')"
return super().preprocess_cell(cell, resources, index, **kwargs)
preprocessor = CellReplacer()
fname = os.path.join(os.path.dirname(__file__), "files", "HelloWorld.ipynb")
with open(fname) as f:
input_nb = nbformat.read(f, 4)
output_nb, _ = preprocessor.preprocess(deepcopy(input_nb))
expected_nb = deepcopy(input_nb)
for cell in expected_nb.cells:
cell.source = "print('Ignored')"
for output in cell.outputs:
output.text = "Ignored\n"
assert_notebooks_equal(expected_nb, output_nb)

View File

@ -0,0 +1,85 @@
"""Tests for the extractoutput preprocessor"""
# Copyright (c) IPython Development Team.
# Distributed under the terms of the Modified BSD License.
import json
from ..extractoutput import ExtractOutputPreprocessor
from .base import PreprocessorTestsBase
class TestExtractOutput(PreprocessorTestsBase):
"""Contains test functions for extractoutput.py"""
def build_preprocessor(self):
"""Make an instance of a preprocessor"""
preprocessor = ExtractOutputPreprocessor()
preprocessor.extract_output_types = {"text/plain", "image/png", "application/pdf"}
preprocessor.enabled = True
return preprocessor
def test_constructor(self):
"""Can a ExtractOutputPreprocessor be constructed?"""
self.build_preprocessor()
def test_output(self):
"""Test the output of the ExtractOutputPreprocessor"""
nb = self.build_notebook()
res = self.build_resources()
preprocessor = self.build_preprocessor()
nb, res = preprocessor(nb, res)
# Check if text was extracted.
output = nb.cells[0].outputs[1]
self.assertIn("filenames", output.metadata)
self.assertIn("text/plain", output.metadata.filenames)
text_filename = output.metadata.filenames["text/plain"]
# Check if png was extracted.
output = nb.cells[0].outputs[6]
self.assertIn("filenames", output.metadata)
self.assertIn("image/png", output.metadata.filenames)
png_filename = output.metadata.filenames["image/png"]
# Check that pdf was extracted
output = nb.cells[0].outputs[7]
self.assertIn("filenames", output.metadata)
self.assertIn("application/pdf", output.metadata.filenames)
pdf_filename = output.metadata.filenames["application/pdf"]
# Verify text output
self.assertIn(text_filename, res["outputs"])
self.assertEqual(res["outputs"][text_filename], b"b")
# Verify png output
self.assertIn(png_filename, res["outputs"])
self.assertEqual(res["outputs"][png_filename], b"g")
# Verify pdf output
self.assertIn(pdf_filename, res["outputs"])
self.assertEqual(res["outputs"][pdf_filename], b"h")
def test_json_extraction(self):
nb = self.build_notebook(with_json_outputs=True)
res = self.build_resources()
preprocessor = self.build_preprocessor()
preprocessor.extract_output_types = {"application/json"}
nb, res = preprocessor(nb, res)
reference = self.build_notebook(with_json_outputs=True).cells[0].outputs
# Verify cell untouched
self.assertEqual(
[out.get("data") for out in nb.cells[0].outputs], [out.get("data") for out in reference]
)
outputs = sorted(res["outputs"].values())
reference_files = []
for out in reference:
try:
data = out["data"]["application/json"]
reference_files.append(json.dumps(data).encode())
except KeyError:
pass
# Verify equivalence of extracted outputs.
self.assertEqual(sorted(outputs), sorted(reference_files))

View File

@ -0,0 +1,53 @@
"""Tests for the HighlightMagics preprocessor"""
from ..highlightmagics import HighlightMagicsPreprocessor
from .base import PreprocessorTestsBase
class TestHighlightMagics(PreprocessorTestsBase):
"""Contains test functions for highlightmagics.py"""
def build_preprocessor(self):
"""Make an instance of a preprocessor"""
preprocessor = HighlightMagicsPreprocessor()
preprocessor.enabled = True
return preprocessor
def test_constructor(self):
"""Can a HighlightMagicsPreprocessor be constructed?"""
self.build_preprocessor()
def test_tagging(self):
"""Test the HighlightMagicsPreprocessor tagging"""
nb = self.build_notebook()
res = self.build_resources()
preprocessor = self.build_preprocessor()
nb.cells[
0
].source = """%%R -i x,y -o XYcoef
lm.fit <- lm(y~x)
par(mfrow=c(2,2))
print(summary(lm.fit))
plot(lm.fit)
XYcoef <- coef(lm.fit)"""
nb, res = preprocessor(nb, res)
assert "magics_language" in nb.cells[0]["metadata"]
self.assertEqual(nb.cells[0]["metadata"]["magics_language"], "r")
def test_no_false_positive(self):
"""Test that HighlightMagicsPreprocessor does not tag false positives"""
nb = self.build_notebook()
res = self.build_resources()
preprocessor = self.build_preprocessor()
nb.cells[
0
].source = """# this should not be detected
print(\"""
%%R -i x, y
\""")"""
nb, res = preprocessor(nb, res)
assert "magics_language" not in nb.cells[0]["metadata"]

View File

@ -0,0 +1,53 @@
"""Tests for the latex preprocessor"""
# Copyright (c) IPython Development Team.
# Distributed under the terms of the Modified BSD License.
from ..latex import LatexPreprocessor
from .base import PreprocessorTestsBase
class TestLatex(PreprocessorTestsBase):
"""Contains test functions for latex.py"""
def build_preprocessor(self):
"""Make an instance of a preprocessor"""
preprocessor = LatexPreprocessor()
preprocessor.enabled = True
return preprocessor
def test_constructor(self):
"""Can a LatexPreprocessor be constructed?"""
self.build_preprocessor()
def test_output(self):
"""Test the output of the LatexPreprocessor"""
nb = self.build_notebook()
res = self.build_resources()
preprocessor = self.build_preprocessor()
nb, res = preprocessor(nb, res)
# Make sure the code cell wasn't modified.
self.assertEqual(nb.cells[0].source, "$ e $")
# Verify that the markdown cell wasn't processed.
self.assertEqual(nb.cells[1].source, "$ e $")
def test_highlight(self):
"""Check that highlighting style can be changed"""
nb = self.build_notebook()
res = self.build_resources()
preprocessor = self.build_preprocessor()
# Set the style to a known builtin that's not the default
preprocessor.style = "colorful"
nb, res = preprocessor(nb, res)
style_defs = res["latex"]["pygments_definitions"]
# Get the default
from pygments.formatters import LatexFormatter
default_defs = LatexFormatter(style="default").get_style_defs()
# Verify that the style was in fact changed
assert style_defs != default_defs

View File

@ -0,0 +1,73 @@
"""
Module with tests for the RegexRemovePreprocessor.
"""
# Copyright (c) IPython Development Team.
# Distributed under the terms of the Modified BSD License.
import re
from nbformat import v4 as nbformat
from ..regexremove import RegexRemovePreprocessor
from .base import PreprocessorTestsBase
class TestRegexRemove(PreprocessorTestsBase):
"""Contains test functions for regexremove.py"""
def build_notebook(self):
notebook = super().build_notebook()
# Add a few empty cells
notebook.cells.extend(
[
nbformat.new_code_cell(""),
nbformat.new_markdown_cell(" "),
nbformat.new_raw_cell("\n"),
nbformat.new_raw_cell("\t"),
]
)
return notebook
def build_preprocessor(self):
"""Make an instance of a preprocessor"""
preprocessor = RegexRemovePreprocessor()
preprocessor.enabled = True
return preprocessor
def test_constructor(self):
"""Can a RegexRemovePreprocessor be constructed?"""
self.build_preprocessor()
def test_output(self):
"""Test the output of the RegexRemovePreprocessor"""
pattern_lookup = {
"disallow_whitespace": [r"\s*\Z"],
"disallow_tab_newline": [r"\t\Z", r"\n\Z"],
}
expected_cell_count = {
"default": 6, # nothing is removed
"disallow_whitespace": 2, # all "empty" cells are removed
"disallow_tab_newline": 4, # cells with tab and newline are removed
"none": 6,
}
for method in ["default", "disallow_whitespace", "disallow_tab_newline", "none"]:
nb = self.build_notebook()
res = self.build_resources()
# Build the preprocessor and extend the list of patterns or use an empty list
preprocessor = self.build_preprocessor()
if method == "none":
preprocessor.patterns = []
else:
preprocessor.patterns.extend(pattern_lookup.get(method, []))
nb, res = preprocessor(nb, res)
self.assertEqual(len(nb.cells), expected_cell_count[method])
# Make sure none of the cells match the pattern
patterns = list(map(re.compile, preprocessor.patterns))
for cell in nb.cells:
for pattern in patterns:
self.assertFalse(pattern.match(cell.source))

View File

@ -0,0 +1,182 @@
"""Tests for the HTMLSanitize preprocessor"""
from nbformat import v4 as nbformat
from ..sanitize import SanitizeHTML
from .base import PreprocessorTestsBase
class TestSanitizer(PreprocessorTestsBase):
"""Contains test functions for sanitize.py"""
maxDiff = None
def build_preprocessor(self):
"""Make an instance of a preprocessor"""
preprocessor = SanitizeHTML()
preprocessor.enabled = True
return preprocessor
def preprocess_source(self, cell_type, source, preprocessor):
nb = self.build_notebook()
res = self.build_resources()
nb.cells[0].cell_type = cell_type
nb.cells[0].source = source
nb, res = preprocessor(nb, res)
return nb.cells[0].source
def test_constructor(self):
"""Can a SanitizeHTML be constructed?"""
self.build_preprocessor()
def test_svg_handling(self):
"""
Test to make sure that svgs are handled 'properly'
We only allow <img> tags (via markdown syntax) and not all the other ways
to embed svg: <object>, <embed>, <iframe> nor inline <svg>
"""
preprocessor = self.build_preprocessor()
preprocessor.strip = True
self.assertEqual(
self.preprocess_source(
"markdown",
"""
![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_allowlist_stripping(self):
"""Test tag allowlisting + stripping out offending tags"""
preprocessor = self.build_preprocessor()
preprocessor.strip = True
self.assertEqual(
self.preprocess_source(
"markdown", "_A_ <em>few</em> <script>tags</script>", preprocessor
),
"_A_ <em>few</em> tags",
)
def test_comment_stripping(self):
"""Test HTML comment stripping"""
preprocessor = self.build_preprocessor()
self.assertEqual(
self.preprocess_source("markdown", "_A_ <em>few</em> <!-- tags -->", preprocessor),
"_A_ <em>few</em> ",
)
preprocessor.strip_comments = False
self.assertEqual(
self.preprocess_source("markdown", "_A_ <em>few</em> <!-- tags -->", preprocessor),
"_A_ <em>few</em> <!-- tags -->",
)
def test_attributes_allowlist(self):
"""Test style"""
preprocessor = self.build_preprocessor()
preprocessor.attributes["a"] = ["href", "title"]
self.assertEqual(
self.preprocess_source(
"markdown", '<a href="link" rel="nofollow">Hi</a>', preprocessor
),
'<a href="link">Hi</a>',
)
def test_style_allowlist(self):
"""Test style"""
preprocessor = self.build_preprocessor()
if "*" in preprocessor.attributes:
preprocessor.attributes["*"].append("style")
else:
preprocessor.attributes["*"] = ["style"]
preprocessor.styles = [
"color",
]
self.assertEqual(
self.preprocess_source(
"markdown",
'_A_ <em style="color: blue; background-color: pink">'
"few</em> <script>tags</script>",
preprocessor,
),
'_A_ <em style="color: blue;">few</em> &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_allowlist(self):
"""Test tag allowlisting"""
preprocessor = self.build_preprocessor()
self.assertEqual(
self.preprocess_source(
"markdown", "_A_ <em>few</em> <script>tags</script>", preprocessor
),
"_A_ <em>few</em> &lt;script&gt;tags&lt;/script&gt;",
)

View File

@ -0,0 +1,100 @@
"""Tests for the svg2pdf preprocessor"""
# Copyright (c) IPython Development Team.
# Distributed under the terms of the Modified BSD License.
from unittest.mock import patch
from nbformat import v4 as nbformat
from ...tests.utils import onlyif_cmds_exist
from ..svg2pdf import SVG2PDFPreprocessor
from .base import PreprocessorTestsBase
class Testsvg2pdf(PreprocessorTestsBase):
"""Contains test functions for svg2pdf.py"""
simple_svg = """<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
version="1.0"
x="0.00000000"
y="0.00000000"
width="500.00000"
height="500.00000"
id="svg2">
<defs
id="defs4" />
<g
id="layer1">
<rect
width="300.00000"
height="300.00000"
x="100.00000"
y="100.00000"
style="opacity:1.0000000;fill:none;fill-opacity:1.0000000;fill-rule:evenodd;stroke:#000000;stroke-width:8.0000000;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4.0000000;stroke-dasharray:none;stroke-dashoffset:0.00000000;stroke-opacity:1.0000000"
id="rect5719" />
</g>
</svg>"""
def build_notebook(self):
"""Build a reveal slides notebook in memory for use with tests.
Overrides base in PreprocessorTestsBase"""
outputs = [
nbformat.new_output(output_type="display_data", data={"image/svg+xml": self.simple_svg})
]
cells = [nbformat.new_code_cell(source="", execution_count=1, outputs=outputs)]
return nbformat.new_notebook(cells=cells)
def build_preprocessor(self, **kwargs):
"""Make an instance of a preprocessor"""
preprocessor = SVG2PDFPreprocessor(**kwargs)
preprocessor.enabled = True
return preprocessor
def test_constructor(self):
"""Can a SVG2PDFPreprocessor be constructed?"""
self.build_preprocessor()
@onlyif_cmds_exist("inkscape")
def test_output(self):
"""Test the output of the SVG2PDFPreprocessor"""
nb = self.build_notebook()
res = self.build_resources()
preprocessor = self.build_preprocessor()
nb, res = preprocessor(nb, res)
self.assertIn("application/pdf", nb.cells[0].outputs[0].data)
@onlyif_cmds_exist("inkscape")
@patch("subprocess.Popen")
def test_inkscape_version_default(self, mock_popen):
mock_popen().communicate.return_value = (b"Inkscape 0.92.3 (2405546, 2018-03-11)", b"")
mock_popen().returncode = 0
preprocessor = self.build_preprocessor()
assert preprocessor.inkscape_version == "0.92.3"
def test_inkscape_pre_v1_command(self):
preprocessor = self.build_preprocessor(inkscape="fake-inkscape", inkscape_version="0.92.3")
assert preprocessor.command == [
"fake-inkscape",
"--without-gui",
"--export-pdf={to_filename}",
"{from_filename}",
]
def test_inkscape_v1_command(self):
preprocessor = self.build_preprocessor(
inkscape="fake-inkscape", inkscape_version="1.0beta2"
)
assert preprocessor.command == [
"fake-inkscape",
"--export-filename={to_filename}",
"{from_filename}",
]

View File

@ -0,0 +1,88 @@
"""
Module with tests for the TagRemovePreprocessor.
"""
# Copyright (c) IPython Development Team.
# Distributed under the terms of the Modified BSD License.
from nbformat import v4 as nbformat
from ..tagremove import TagRemovePreprocessor
from .base import PreprocessorTestsBase
class TestTagRemove(PreprocessorTestsBase):
"""Contains test functions for tagremove.py"""
def build_notebook(self):
"""
Build a notebook to have metadata tags for cells, output_areas, and
individual outputs.
"""
notebook = super().build_notebook()
# Add a few empty cells
notebook.cells[0].outputs.extend(
[
nbformat.new_output(
"display_data", data={"text/plain": "i"}, metadata={"tags": ["hide_one_output"]}
),
]
)
outputs_to_be_removed = [
nbformat.new_output("display_data", data={"text/plain": "remove_my_output"}),
]
outputs_to_be_kept = [
nbformat.new_output(
"stream",
name="stdout",
text="remove_my_output",
),
]
notebook.cells.extend(
[
nbformat.new_code_cell(
source="display('remove_my_output')",
execution_count=2,
outputs=outputs_to_be_removed,
metadata={"tags": ["hide_all_outputs"]},
),
nbformat.new_code_cell(
source="print('remove this cell')",
execution_count=3,
outputs=outputs_to_be_kept,
metadata={"tags": ["hide_this_cell"]},
),
]
)
return notebook
def build_preprocessor(self):
"""Make an instance of a preprocessor"""
preprocessor = TagRemovePreprocessor()
preprocessor.enabled = True
return preprocessor
def test_constructor(self):
"""Can a TagRemovePreprocessor be constructed?"""
self.build_preprocessor()
def test_output(self):
"""Test the output of the TagRemovePreprocessor"""
nb = self.build_notebook()
res = self.build_resources()
preprocessor = self.build_preprocessor()
preprocessor.remove_cell_tags.add("hide_this_cell")
preprocessor.remove_all_outputs_tags.add("hide_all_outputs")
preprocessor.remove_single_output_tags.add("hide_one_output")
nb, res = preprocessor(nb, res)
# checks that we can remove entire cells
self.assertEqual(len(nb.cells), 3)
# checks that we can remove output areas
self.assertEqual(len(nb.cells[-1].outputs), 0)
# checks that we can remove individual outputs
self.assertEqual(len(nb.cells[0].outputs), 8)