Skip to content

Commit f636c5f

Browse files
authored
Simplify dev_tools.notebooks.utils.rewrite_notebook (#6030)
* Simplify dev_tools.notebooks.utils.rewrite_notebook - Do not return the OS file descriptor, it was only used to close the file in rewrite_notebook callers - Return only the filename of the rewritten notebook - Always create a temporary file, even if identical to the input notebook, so it can be safely removed after a test - Clean up temporary files produced by rewrite_notebook in the tests
1 parent 8b97aa5 commit f636c5f

File tree

4 files changed

+35
-39
lines changed

4 files changed

+35
-39
lines changed

dev_tools/notebooks/isolated_notebook_test.py

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -177,7 +177,7 @@ def test_notebooks_against_released_cirq(partition, notebook_path, cloned_env):
177177

178178
notebook_file = os.path.basename(notebook_path)
179179

180-
rewritten_notebook_descriptor, rewritten_notebook_path = rewrite_notebook(notebook_path)
180+
rewritten_notebook_path = rewrite_notebook(notebook_path)
181181

182182
cmd = f"""
183183
mkdir -p out/{notebook_rel_dir}
@@ -208,9 +208,7 @@ def test_notebooks_against_released_cirq(partition, notebook_path, cloned_env):
208208
f"instead of `pip install cirq` to this notebook, and exclude it from "
209209
f"dev_tools/notebooks/isolated_notebook_test.py."
210210
)
211-
212-
if rewritten_notebook_descriptor:
213-
os.close(rewritten_notebook_descriptor)
211+
os.remove(rewritten_notebook_path)
214212

215213

216214
@pytest.mark.parametrize("notebook_path", NOTEBOOKS_DEPENDING_ON_UNRELEASED_FEATURES)

dev_tools/notebooks/notebook_test.py

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ def test_notebooks_against_released_cirq(notebook_path):
6767
notebook_file = os.path.basename(notebook_path)
6868
notebook_rel_dir = os.path.dirname(os.path.relpath(notebook_path, "."))
6969
out_path = f"out/{notebook_rel_dir}/{notebook_file[:-6]}.out.ipynb"
70-
rewritten_notebook_descriptor, rewritten_notebook_path = rewrite_notebook(notebook_path)
70+
rewritten_notebook_path = rewrite_notebook(notebook_path)
7171
papermill_flags = "--no-request-save-on-cell-execute --autosave-cell-every 0"
7272
cmd = f"""mkdir -p out/{notebook_rel_dir}
7373
papermill {rewritten_notebook_path} {out_path} {papermill_flags}"""
@@ -83,6 +83,4 @@ def test_notebooks_against_released_cirq(notebook_path):
8383
f"notebook (in Github Actions, you can download it from the workflow artifact"
8484
f" 'notebook-outputs')"
8585
)
86-
87-
if rewritten_notebook_descriptor:
88-
os.close(rewritten_notebook_descriptor)
86+
os.remove(rewritten_notebook_path)

dev_tools/notebooks/utils.py

Lines changed: 20 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ def rewrite_notebook(notebook_path):
6969
7070
* Lines in this file without `->` are ignored.
7171
72-
* Lines in this file with `->` are split into two (if there are mulitple `->` it is an
72+
* Lines in this file with `->` are split into two (if there are multiple `->` it is an
7373
error). The first of these is compiled into a pattern match, via `re.compile`, and
7474
the second is the replacement for that match.
7575
@@ -82,42 +82,41 @@ def rewrite_notebook(notebook_path):
8282
It is the responsibility of the caller of this method to delete the new file.
8383
8484
Returns:
85-
Tuple of a file descriptor and the file path for the rewritten file. If no `.tst` file
86-
was found, then the file descriptor is None and the path is `notebook_path`.
85+
The absolute path to the rewritten file in temporary directory.
86+
If no `.tst` file exists the new file is a copy of the input notebook.
8787
8888
Raises:
8989
AssertionError: If there are multiple `->` per line, or not all of the replacements
9090
are used.
9191
"""
92-
notebook_test_path = os.path.splitext(notebook_path)[0] + '.tst'
93-
if not os.path.exists(notebook_test_path):
94-
return None, notebook_path
95-
9692
# Get the rewrite rules.
9793
patterns = []
98-
with open(notebook_test_path, 'r') as f:
99-
for line in f:
100-
if '->' in line:
94+
notebook_test_path = os.path.splitext(notebook_path)[0] + '.tst'
95+
if os.path.exists(notebook_test_path):
96+
with open(notebook_test_path, 'r') as f:
97+
pattern_lines = (line for line in f if '->' in line)
98+
for line in pattern_lines:
10199
parts = line.rstrip().split('->')
102100
assert len(parts) == 2, f'Replacement lines may only contain one -> but was {line}'
103101
patterns.append((re.compile(parts[0]), parts[1]))
104102

105103
used_patterns = set()
106104
with open(notebook_path, 'r') as original_file:
107-
new_file_descriptor, new_file_path = tempfile.mkstemp(suffix='.ipynb')
108-
with open(new_file_path, 'w') as new_file:
109-
for line in original_file:
110-
new_line = line
111-
for pattern, replacement in patterns:
112-
new_line = pattern.sub(replacement, new_line)
113-
if new_line != line:
114-
used_patterns.add(pattern)
115-
break
116-
new_file.write(new_line)
105+
lines = original_file.readlines()
106+
for i, line in enumerate(lines):
107+
for pattern, replacement in patterns:
108+
new_line = pattern.sub(replacement, line)
109+
if new_line != line:
110+
lines[i] = new_line
111+
used_patterns.add(pattern)
112+
break
117113

118114
assert len(patterns) == len(used_patterns), (
119115
'Not all patterns where used. Patterns not used: '
120116
f'{set(x for x, _ in patterns) - used_patterns}'
121117
)
122118

123-
return new_file_descriptor, new_file_path
119+
with tempfile.NamedTemporaryFile(mode='w', suffix='-rewrite.ipynb', delete=False) as new_file:
120+
new_file.writelines(lines)
121+
122+
return new_file.name

dev_tools/notebooks/utils_test.py

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
# See the License for the specific language governing permissions and
1313
# limitations under the License.
1414

15+
import filecmp
1516
import os
1617
import shutil
1718
import tempfile
@@ -37,40 +38,40 @@ def write_test_data(ipynb_txt, tst_txt):
3738
def test_rewrite_notebook():
3839
directory, ipynb_path = write_test_data('d = 5\nd = 4', 'd = 5->d = 3')
3940

40-
descriptor, path = dt.rewrite_notebook(ipynb_path)
41+
path = dt.rewrite_notebook(ipynb_path)
4142

4243
assert path != ipynb_path
4344
with open(path, 'r') as f:
4445
rewritten = f.read()
4546
assert rewritten == 'd = 3\nd = 4'
4647

47-
os.close(descriptor)
48+
os.remove(path)
4849
shutil.rmtree(directory)
4950

5051

5152
def test_rewrite_notebook_multiple():
5253
directory, ipynb_path = write_test_data('d = 5\nd = 4', 'd = 5->d = 3\nd = 4->d = 1')
5354

54-
descriptor, path = dt.rewrite_notebook(ipynb_path)
55+
path = dt.rewrite_notebook(ipynb_path)
5556

5657
with open(path, 'r') as f:
5758
rewritten = f.read()
5859
assert rewritten == 'd = 3\nd = 1'
5960

60-
os.close(descriptor)
61+
os.remove(path)
6162
shutil.rmtree(directory)
6263

6364

6465
def test_rewrite_notebook_ignore_non_seperator_lines():
6566
directory, ipynb_path = write_test_data('d = 5\nd = 4', 'd = 5->d = 3\n# comment')
6667

67-
descriptor, path = dt.rewrite_notebook(ipynb_path)
68+
path = dt.rewrite_notebook(ipynb_path)
6869

6970
with open(path, 'r') as f:
7071
rewritten = f.read()
7172
assert rewritten == 'd = 3\nd = 4'
7273

73-
os.close(descriptor)
74+
os.remove(path)
7475
shutil.rmtree(directory)
7576

7677

@@ -80,11 +81,11 @@ def test_rewrite_notebook_no_tst_file():
8081
with open(ipynb_path, 'w') as f:
8182
f.write('d = 5\nd = 4')
8283

83-
descriptor, path = dt.rewrite_notebook(ipynb_path)
84-
85-
assert descriptor is None
86-
assert path == ipynb_path
84+
path = dt.rewrite_notebook(ipynb_path)
85+
assert path != ipynb_path
86+
assert filecmp.cmp(path, ipynb_path)
8787

88+
os.remove(path)
8889
shutil.rmtree(directory)
8990

9091

0 commit comments

Comments
 (0)