Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Tests which fail in multiprocessing contexts #1018

Merged
merged 25 commits into from
Jul 8, 2019
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
84108d8
Parallel Demonstrator Tests
alexrudy May 8, 2019
be4ee65
Mark tests as XFail
alexrudy May 8, 2019
b13601f
Parallel Demonstrator Tests
alexrudy May 8, 2019
f4752c0
Mark tests as XFail
alexrudy May 8, 2019
a08d359
Applied PR feedback
MSeal Jun 12, 2019
6a0f8c8
Merge remote-tracking branch 'rudy/fix-zmq-context-global' into fix-zmq
MSeal Jun 12, 2019
55bb0db
Removed duplicate tests from merge
MSeal Jun 12, 2019
cc0ac44
Merge branch 'master' of github.com:jupyter/nbconvert into fix-zmq
MSeal Jun 12, 2019
5c0848f
Added additional timeout delay to test
MSeal Jun 12, 2019
b88f3e9
Set additional timeout on the correct test field
MSeal Jun 12, 2019
a7a92b1
Added ability to turn off slow tests
MSeal Jun 12, 2019
ae011e7
Attempt to get travis passing slow tests
MSeal Jun 12, 2019
a443d03
Fixing pytest conf issues with temp directories
MSeal Jun 12, 2019
d1a3a6d
Moved conftest into nbconvert path
MSeal Jun 12, 2019
4d3e992
Removed unecessary travis command
MSeal Jun 12, 2019
48beaa4
Another attempt to get Travis
MSeal Jun 12, 2019
baea777
Simplified the test execution
MSeal Jun 12, 2019
f81e1a2
Yet another travis fix attempt
MSeal Jun 12, 2019
1434a2a
Adding much higher timeouts to failing test
MSeal Jun 13, 2019
a818745
Attempt #billion to fix travis
MSeal Jun 13, 2019
bcd5157
Travis only-failure debug
MSeal Jun 13, 2019
f74f2e1
Added latest jupyter_client to travis for test run
MSeal Jun 13, 2019
c3ef4b7
Removed extra debug lines
MSeal Jun 13, 2019
9bbcecb
Resolve conflict with master
MSeal Jul 8, 2019
70d47b7
Removed jupyter_client master install from travis
MSeal Jul 8, 2019
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,11 @@ htmlcov
.cache
docs/source/*.tpl
docs/source/config_options.rst

# Eclipse pollutes the filesystem
.project
.pydevproject
.settings

# VSCode
.vscode
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
"This notebook uses a file system based \"lock\" to assert that two instances of the notebook kernel will run in parallel. Each instance writes to a file in a temporary directory, and then tries to read the other file from\n",
"the temporary directory, so that running them in sequence will fail, but running them in parallel will succed.\n",
"\n",
"Two notebooks are launched, each with an injected cell which sets the `this_notebook` variable. One notebook is set to `this_notebook = 'A'` and the other `this_notebook = 'B'`."
"Two notebooks are launched, each which sets the `this_notebook` variable. One notebook is set to `this_notebook = 'A'` and the other `this_notebook = 'B'`."
]
},
{
Expand All @@ -31,7 +31,8 @@
"outputs": [],
"source": [
"# the variable this_notebook is injectected in a cell above by the test framework.\n",
"other_notebook = {'A':'B', 'B':'A'}[this_notebook]\n",
"this_notebook = 'A'\n",
"other_notebook = 'B'\n",
"directory = os.environ['NBEXECUTE_TEST_PARALLEL_TMPDIR']\n",
"with open(os.path.join(directory, 'test_file_{}.txt'.format(this_notebook)), 'w') as f:\n",
" f.write('Hello from {}'.format(this_notebook))"
Expand Down Expand Up @@ -59,7 +60,25 @@
]
}
],
"metadata": {},
"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.6.7"
}
},
"nbformat": 4,
"nbformat_minor": 2
}
84 changes: 84 additions & 0 deletions nbconvert/preprocessors/tests/files/Parallel Execute B.ipynb
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Ensure notebooks can execute in parallel\n",
"\n",
"This notebook uses a file system based \"lock\" to assert that two instances of the notebook kernel will run in parallel. Each instance writes to a file in a temporary directory, and then tries to read the other file from\n",
"the temporary directory, so that running them in sequence will fail, but running them in parallel will succed.\n",
"\n",
"Two notebooks are launched, each which sets the `this_notebook` variable. One notebook is set to `this_notebook = 'A'` and the other `this_notebook = 'B'`."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"import os\n",
"import os.path\n",
"import tempfile\n",
"import time"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# the variable this_notebook is injectected in a cell above by the test framework.\n",
"this_notebook = 'B'\n",
"other_notebook = 'A'\n",
"directory = os.environ['NBEXECUTE_TEST_PARALLEL_TMPDIR']\n",
"with open(os.path.join(directory, 'test_file_{}.txt'.format(this_notebook)), 'w') as f:\n",
" f.write('Hello from {}'.format(this_notebook))"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"start = time.time()\n",
"timeout = 5\n",
"end = start + timeout\n",
"target_file = os.path.join(directory, 'test_file_{}.txt'.format(other_notebook))\n",
"while time.time() < end:\n",
" time.sleep(0.1)\n",
" if os.path.exists(target_file):\n",
" with open(target_file, 'r') as f:\n",
" text = f.read()\n",
" if text == 'Hello from {}'.format(other_notebook):\n",
" break\n",
"else:\n",
" assert False, \"Timed out – didn't get a message from {}\".format(other_notebook)"
]
}
],
"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.6.7"
}
},
"nbformat": 4,
"nbformat_minor": 2
}
50 changes: 50 additions & 0 deletions nbconvert/preprocessors/tests/files/Sleep One.ipynb
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
{
"cells": [
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"import time"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"time.sleep(0.01)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
}
],
"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.7.0"
}
},
"nbformat": 4,
"nbformat_minor": 2
}
57 changes: 39 additions & 18 deletions nbconvert/preprocessors/tests/test_execute.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
from nbconvert.filters import strip_ansi
from testpath import modified_env
from ipython_genutils.py3compat import string_types
from pebble import ProcessPool

try:
from queue import Empty # Py 3
Expand All @@ -46,6 +47,11 @@
except ImportError:
from mock import MagicMock, patch # Py 2


PY3 = False
if sys.version_info[0] >= 3:
PY3 = True

addr_pat = re.compile(r'0x[0-9a-f]{7,9}')
ipython_input_pat = re.compile(r'<ipython-input-\d+-[0-9a-f]+>')
current_dir = os.path.dirname(__file__)
Expand Down Expand Up @@ -74,17 +80,14 @@ def build_preprocessor(opts):
return preprocessor


def run_notebook(filename, opts, resources, preprocess_notebook=None):
def run_notebook(filename, opts, resources):
"""Loads and runs a notebook, returning both the version prior to
running it and the version after running it.

"""
with io.open(filename) as f:
input_nb = nbformat.read(f, 4)

if preprocess_notebook:
input_nb = preprocess_notebook(input_nb)

preprocessor = build_preprocessor(opts)
cleaned_input_nb = copy.deepcopy(input_nb)
for cell in cleaned_input_nb.cells:
Expand Down Expand Up @@ -264,24 +267,14 @@ def test_run_all_notebooks(input_name, opts):
assert_notebooks_equal(input_nb, output_nb)


def label_parallel_notebook(nb, label):
"""Insert a cell in a notebook which sets the variable `this_notebook` to the string `label`.

Used for parallel testing to label two notebooks which are run simultaneously.
"""
label_cell = nbformat.v4.new_code_cell(source="this_notebook = '{}'".format(label))
nb.cells.insert(1, label_cell)
return nb


def test_parallel_notebooks(capfd, tmpdir):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are capfd and tmpdir pytest fixtures? Otherwise, where are these being populated when it comes time to test them?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"""Two notebooks should be able to be run simultaneously without problems.

The two notebooks spawned here use the filesystem to check that the other notebook
wrote to the filesystem."""

opts = dict(kernel_name="python")
input_name = "Parallel Execute.ipynb"
input_name = "Parallel Execute {label}.ipynb"
input_file = os.path.join(current_dir, "files", input_name)
res = notebook_resources()

Expand All @@ -290,10 +283,9 @@ def test_parallel_notebooks(capfd, tmpdir):
threading.Thread(
target=run_notebook,
args=(
input_file,
input_file.format(label=label),
opts,
res,
functools.partial(label_parallel_notebook, label=label),
),
)
for label in ("A", "B")
Expand All @@ -304,6 +296,35 @@ def test_parallel_notebooks(capfd, tmpdir):
captured = capfd.readouterr()
assert captured.err == ""

@pytest.mark.skipif(not PY3,
reason = "Not tested for Python 2")
def test_many_parallel_notebooks(capfd):
"""Ensure that when many IPython kernels are run in parallel, nothing awful happens.

Specifically, many IPython kernels when run simultaneously would enocunter errors
due to using the same SQLite history database.
"""

# I've put timeout=5, which is a bit aggressive, but in testing it proved to fail
opts = dict(kernel_name="python", timeout=5)
input_name = "HelloWorld.ipynb"
input_file = os.path.join(current_dir, "files", input_name)
res = PreprocessorTestsBase().build_resources()
res["metadata"]["path"] = os.path.join(current_dir, "files")

# run once, to trigger creating the original context
run_notebook(input_file, opts, res)

with ProcessPool(max_workers=4) as pool:
futures = [
pool.schedule(run_notebook, args=(input_file, opts, res), timeout=10)
for i in range(0, 8)
]
for index, future in enumerate(futures):
future.result()

captured = capfd.readouterr()
assert captured.err == ""

class TestExecute(PreprocessorTestsBase):
"""Contains test functions for execute.py"""
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -212,7 +212,7 @@ def run(self):
jupyter_client_req = 'jupyter_client>=4.2'

extra_requirements = {
'test': ['pytest', 'pytest-cov', 'mock', 'ipykernel', jupyter_client_req, 'ipywidgets>=7'],
'test': ['pytest', 'pytest-cov', 'mock', 'ipykernel', jupyter_client_req, 'ipywidgets>=7', 'pebble'],
'serve': ['tornado>=4.0'],
'execute': [jupyter_client_req],
'docs': ['sphinx>=1.5.1',
Expand Down