-
Notifications
You must be signed in to change notification settings - Fork 1k
/
notebook_test.py
136 lines (120 loc) · 5.6 KB
/
notebook_test.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
# Copyright 2020 The Cirq Developers
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ========================== CONTINUOUS NOTEBOOK TESTS ============================================
#
# These tests are run for all of our notebooks against the current branch. It is assumed that
# notebooks will not install cirq in case cirq is on the path. The simple `import cirq` path is the
# main focus and it is executed in a shared virtual environment for the notebooks. Thus, these
# tests ensure that notebooks are still working with the latest version of cirq.
import importlib.metadata
import os
import tempfile
import pytest
from dev_tools import shell_tools
from dev_tools.modules import list_modules
from dev_tools.notebooks import filter_notebooks, list_all_notebooks, rewrite_notebook
from dev_tools.test_utils import only_on_posix
SKIP_NOTEBOOKS = [
# skipping vendor notebooks as we don't have auth sorted out
'**/aqt/*.ipynb',
'**/azure-quantum/*.ipynb',
'**/ionq/*.ipynb',
'**/pasqal/*.ipynb',
'**/rigetti/*.ipynb',
# disabled to unblock Python 3.12. TODO(#6590) - fix and enable.
'cirq-core/cirq/contrib/quimb/Contract-a-Grid-Circuit.ipynb',
# skipping fidelity estimation due to
# skipping quantum utility simulation (too large)
'examples/advanced/*quantum_utility*',
# tutorials that use QCS and arent skipped due to one or more cleared output cells
'docs/tutorials/google/identifying_hardware_changes.ipynb',
'docs/tutorials/google/echoes.ipynb',
'docs/noise/qcvv/xeb_calibration_example.ipynb',
'docs/noise/calibration_api.ipynb',
'docs/noise/floquet_calibration_example.ipynb',
# temporary: need to fix QVM metrics and device spec
'docs/tutorials/google/spin_echoes.ipynb',
'docs/tutorials/google/visualizing_calibration_metrics.ipynb',
]
@pytest.fixture
def require_packages_not_changed():
"""Verify notebook test does not change packages in the Python test environment.
Raise AssertionError if the pre-existing set of Python packages changes in any way.
"""
cirq_packages = set(m.name for m in list_modules()).union(["cirq"])
packages_before = set(
(d.name, d.version)
for d in importlib.metadata.distributions()
if d.name not in cirq_packages
)
yield
packages_after = set(
(d.name, d.version)
for d in importlib.metadata.distributions()
if d.name not in cirq_packages
)
assert packages_after == packages_before
@pytest.fixture
def env_with_temporary_pip_target():
"""Setup system environment that tells pip to install packages to a temporary directory."""
with tempfile.TemporaryDirectory(suffix='-notebook-site-packages') as tmpdirname:
# Note: We need to append tmpdirname to the PYTHONPATH, because PYTHONPATH may
# already point to the development sources of Cirq (as happens with check/pytest).
# Should some notebook pip-install a stable version of Cirq to tmpdirname,
# it would appear in PYTHONPATH after the development Cirq.
pythonpath = (
f'{os.environ["PYTHONPATH"]}{os.pathsep}{tmpdirname}'
if 'PYTHONPATH' in os.environ
else tmpdirname
)
env = {**os.environ, 'PYTHONPATH': pythonpath, 'PIP_TARGET': tmpdirname}
yield env
@pytest.mark.slow
@only_on_posix
@pytest.mark.parametrize("notebook_path", filter_notebooks(list_all_notebooks(), SKIP_NOTEBOOKS))
def test_notebooks_against_cirq_head(
notebook_path, require_packages_not_changed, env_with_temporary_pip_target
):
"""Test that jupyter notebooks execute.
In order to speed up the execution of these tests an auxiliary file may be supplied which
performs substitutions on the notebook to make it faster.
Specifically for a notebook file notebook.ipynb, one can supply a file notebook.tst which
contains the substitutes. The substitutions are provide in the form `pattern->replacement`
where the pattern is what is matched and replaced. While the pattern is compiled as a
regular expression, it is considered best practice to not use complicated regular expressions.
Lines in this file that do not have `->` are ignored.
"""
notebook_file = os.path.basename(notebook_path)
notebook_rel_dir = os.path.dirname(os.path.relpath(notebook_path, "."))
out_path = f"out/{notebook_rel_dir}/{notebook_file[:-6]}.out.ipynb"
rewritten_notebook_path = rewrite_notebook(notebook_path)
papermill_flags = "--no-request-save-on-cell-execute --autosave-cell-every 0"
cmd = f"""mkdir -p out/{notebook_rel_dir}
papermill {rewritten_notebook_path} {out_path} {papermill_flags}"""
result = shell_tools.run(
cmd,
log_run_to_stderr=False,
shell=True,
check=False,
capture_output=True,
env=env_with_temporary_pip_target,
)
if result.returncode != 0:
print(result.stderr)
pytest.fail(
f"Notebook failure: {notebook_file}, please see {out_path} for the output "
f"notebook (in GitHub Actions, you can download it from the workflow artifact"
f" 'notebook-outputs')"
)
os.remove(rewritten_notebook_path)