2020-03-04 13:46:06 +01:00
|
|
|
#!/usr/bin/env python3
|
2025-01-10 11:35:44 +01:00
|
|
|
# Copyright 2010-2025 Google LLC
|
2022-06-17 14:23:05 +02:00
|
|
|
# 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
|
|
|
|
|
#
|
|
|
|
|
# http://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.
|
|
|
|
|
|
2020-03-09 11:48:13 +01:00
|
|
|
"""Transform any Python sample or example to Python NoteBook."""
|
2018-08-03 10:13:16 +02:00
|
|
|
import ast
|
2020-09-27 15:38:49 +02:00
|
|
|
import os
|
2022-04-13 14:32:40 +02:00
|
|
|
import re
|
2022-05-16 17:44:50 +02:00
|
|
|
import sys
|
|
|
|
|
|
2020-03-09 11:48:13 +01:00
|
|
|
from nbformat import v3
|
|
|
|
|
from nbformat import v4
|
2017-11-20 17:04:25 +01:00
|
|
|
|
2020-03-09 11:48:13 +01:00
|
|
|
input_file = sys.argv[1]
|
2024-08-12 13:05:21 +02:00
|
|
|
print(f"reading {input_file}")
|
2025-02-04 18:03:49 +01:00
|
|
|
with open(input_file, encoding="utf-8") as fpin:
|
2024-08-12 13:05:21 +02:00
|
|
|
text = fpin.read()
|
2017-11-20 17:04:25 +01:00
|
|
|
|
2020-09-27 15:38:49 +02:00
|
|
|
# Compute output file path.
|
|
|
|
|
output_file = input_file
|
2024-08-12 13:05:21 +02:00
|
|
|
output_file = output_file.replace(".py", ".ipynb")
|
2020-09-27 15:38:49 +02:00
|
|
|
# For example/python/foo.py -> example/notebook/examples/foo.ipynb
|
2024-08-12 13:05:21 +02:00
|
|
|
output_file = output_file.replace("examples/python", "examples/notebook/examples")
|
2020-09-27 15:38:49 +02:00
|
|
|
# For example/contrib/foo.py -> example/notebook/contrib/foo.ipynb
|
2024-08-12 13:05:21 +02:00
|
|
|
output_file = output_file.replace("examples/contrib", "examples/notebook/contrib")
|
2020-09-27 15:38:49 +02:00
|
|
|
# For ortools/*/samples/foo.py -> example/notebook/*/foo.ipynb
|
2024-08-12 13:05:21 +02:00
|
|
|
output_file = output_file.replace("ortools", "examples/notebook")
|
|
|
|
|
output_file = output_file.replace("samples/", "")
|
2020-09-27 15:38:49 +02:00
|
|
|
|
2024-08-12 13:05:21 +02:00
|
|
|
nbook = v3.reads_py("")
|
2017-11-20 17:04:25 +01:00
|
|
|
nbook = v4.upgrade(nbook) # Upgrade v3 to v4
|
|
|
|
|
|
2025-02-04 18:03:49 +01:00
|
|
|
METADATA = {"language_info": {"name": "python"}}
|
|
|
|
|
nbook["metadata"] = METADATA
|
|
|
|
|
|
2024-08-12 13:05:21 +02:00
|
|
|
print("Adding copyright cell...")
|
2025-02-04 18:03:49 +01:00
|
|
|
GOOGLE = "##### Copyright 2025 Google LLC."
|
|
|
|
|
nbook["cells"].append(v4.new_markdown_cell(source=GOOGLE, id="google"))
|
2020-09-27 15:38:49 +02:00
|
|
|
|
2024-08-12 13:05:21 +02:00
|
|
|
print("Adding license cell...")
|
2025-02-04 18:03:49 +01:00
|
|
|
APACHE = """Licensed under the Apache License, Version 2.0 (the "License");
|
2020-09-27 15:38:49 +02:00
|
|
|
you may not use this file except in compliance with the License.
|
|
|
|
|
You may obtain a copy of the License at
|
|
|
|
|
|
|
|
|
|
http://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.
|
2024-08-12 13:05:21 +02:00
|
|
|
"""
|
2025-02-04 18:03:49 +01:00
|
|
|
nbook["cells"].append(v4.new_markdown_cell(source=APACHE, id="apache"))
|
2024-08-12 13:05:21 +02:00
|
|
|
|
|
|
|
|
print("Adding Title cell...")
|
|
|
|
|
basename = "# " + os.path.basename(input_file).replace(".py", "")
|
|
|
|
|
nbook["cells"].append(v4.new_markdown_cell(source=basename, id="basename"))
|
|
|
|
|
|
|
|
|
|
print("Adding link cell...")
|
2025-02-04 18:03:49 +01:00
|
|
|
GITHUB_LOGO = (
|
2024-08-12 13:05:21 +02:00
|
|
|
"https://raw.githubusercontent.com/google/or-tools/main/tools/github_32px.png"
|
|
|
|
|
)
|
2025-02-04 18:03:49 +01:00
|
|
|
GITHUB_PATH = "https://github.com/google/or-tools/blob/main/" + input_file
|
2024-08-12 13:05:21 +02:00
|
|
|
|
2025-02-04 18:03:49 +01:00
|
|
|
COLAB_PATH = (
|
2024-08-12 13:05:21 +02:00
|
|
|
"https://colab.research.google.com/github/google/or-tools/blob/main/" + output_file
|
|
|
|
|
)
|
2025-02-04 18:03:49 +01:00
|
|
|
COLAB_LOGO = (
|
2024-08-12 13:05:21 +02:00
|
|
|
"https://raw.githubusercontent.com/google/or-tools/main/tools/colab_32px.png"
|
|
|
|
|
)
|
|
|
|
|
link = f"""<table align=\"left\">
|
2020-09-27 15:38:49 +02:00
|
|
|
<td>
|
2025-02-04 18:03:49 +01:00
|
|
|
<a href=\"{COLAB_PATH}\"><img src=\"{COLAB_LOGO}\"/>Run in Google Colab</a>
|
2020-09-27 15:38:49 +02:00
|
|
|
</td>
|
|
|
|
|
<td>
|
2025-02-04 18:03:49 +01:00
|
|
|
<a href=\"{GITHUB_PATH}\"><img src=\"{GITHUB_LOGO}\"/>View source on GitHub</a>
|
2020-09-27 15:38:49 +02:00
|
|
|
</td>
|
2024-08-12 13:05:21 +02:00
|
|
|
</table>"""
|
|
|
|
|
nbook["cells"].append(v4.new_markdown_cell(source=link, id="link"))
|
|
|
|
|
|
|
|
|
|
print("Adding ortools install cell...")
|
2025-02-04 18:03:49 +01:00
|
|
|
INSTALL_DOC = (
|
2024-08-12 13:05:21 +02:00
|
|
|
"First, you must install "
|
|
|
|
|
"[ortools](https://pypi.org/project/ortools/) package in this "
|
|
|
|
|
"colab."
|
|
|
|
|
)
|
2025-02-04 18:03:49 +01:00
|
|
|
nbook["cells"].append(v4.new_markdown_cell(source=INSTALL_DOC, id="doc"))
|
|
|
|
|
INSTALL_CMD = "%pip install ortools"
|
|
|
|
|
nbook["cells"].append(v4.new_code_cell(source=INSTALL_CMD, id="install"))
|
2024-08-12 13:05:21 +02:00
|
|
|
|
|
|
|
|
print("Adding code cell...")
|
2018-08-03 10:13:16 +02:00
|
|
|
all_blocks = ast.parse(text).body
|
2024-08-12 13:05:21 +02:00
|
|
|
print(f"number of blocks: {len(all_blocks)}")
|
2020-03-09 11:48:13 +01:00
|
|
|
line_start = [c.lineno - 1 for c in all_blocks]
|
2018-08-03 10:26:25 +02:00
|
|
|
line_start[0] = 0
|
2024-08-12 13:05:21 +02:00
|
|
|
lines = text.split("\n")
|
2018-08-03 10:13:16 +02:00
|
|
|
|
2025-02-04 18:03:49 +01:00
|
|
|
FULL_TEXT = ""
|
2022-04-13 14:32:40 +02:00
|
|
|
for idx, (c_block, s, e) in enumerate(
|
2024-08-12 13:05:21 +02:00
|
|
|
zip(all_blocks, line_start, line_start[1:] + [len(lines)])
|
|
|
|
|
):
|
|
|
|
|
print(f"block[{idx}]: {c_block}")
|
|
|
|
|
c_text = "\n".join(lines[s:e])
|
|
|
|
|
# Clean boilerplate header and description
|
|
|
|
|
if (
|
|
|
|
|
idx == 0
|
|
|
|
|
and isinstance(c_block, ast.Expr)
|
|
|
|
|
and isinstance(c_block.value, ast.Constant)
|
|
|
|
|
):
|
|
|
|
|
print("Adding description cell...")
|
|
|
|
|
filtered_lines = lines[s:e]
|
|
|
|
|
# filtered_lines = list(
|
|
|
|
|
# filter(lambda l: not l.startswith('#!'), lines[s:e]))
|
|
|
|
|
filtered_lines = list(
|
|
|
|
|
filter(lambda l: not re.search(r"^#!", l), filtered_lines)
|
|
|
|
|
)
|
|
|
|
|
filtered_lines = list(
|
|
|
|
|
filter(lambda l: not re.search(r"# \[START .*\]$", l), filtered_lines)
|
|
|
|
|
)
|
|
|
|
|
filtered_lines = list(
|
|
|
|
|
filter(lambda l: not re.search(r"# \[END .*\]$", l), filtered_lines)
|
|
|
|
|
)
|
|
|
|
|
# TODO(user): Remove only copyright not all line with '^#'
|
|
|
|
|
filtered_lines = list(filter(lambda l: not l.startswith(r"#"), filtered_lines))
|
|
|
|
|
filtered_lines = [s.replace(r'"""', "") for s in filtered_lines]
|
|
|
|
|
filtered_text = "\n".join(filtered_lines)
|
|
|
|
|
nbook["cells"].append(
|
|
|
|
|
v4.new_markdown_cell(source=filtered_text, id="description")
|
|
|
|
|
)
|
|
|
|
|
# Remove absl app import
|
|
|
|
|
elif (
|
|
|
|
|
isinstance(c_block, ast.ImportFrom)
|
|
|
|
|
and c_block.module == "absl"
|
|
|
|
|
and c_block.names[0].name == "app"
|
|
|
|
|
):
|
|
|
|
|
print(f"Removing import {c_block.module}.{c_block.names[0].name}...")
|
|
|
|
|
# rewrite absl flag import
|
|
|
|
|
elif (
|
|
|
|
|
isinstance(c_block, ast.ImportFrom)
|
|
|
|
|
and c_block.module == "absl"
|
|
|
|
|
and c_block.names[0].name == "flags"
|
|
|
|
|
):
|
|
|
|
|
print(f"Rewrite import {c_block.module}.{c_block.names[0].name}...")
|
2025-02-04 18:03:49 +01:00
|
|
|
FULL_TEXT += "from ortools.sat.colab import flags\n"
|
2024-08-12 13:05:21 +02:00
|
|
|
# Unwrap __main__ function
|
|
|
|
|
elif isinstance(c_block, ast.If) and c_block.test.comparators[0].s == "__main__":
|
|
|
|
|
print("Unwrapping main function...")
|
|
|
|
|
c_lines = lines[s + 1 : e]
|
|
|
|
|
# remove start and de-indent lines
|
|
|
|
|
spaces_to_delete = c_block.body[0].col_offset
|
|
|
|
|
fixed_lines = [
|
|
|
|
|
(
|
|
|
|
|
n_line[spaces_to_delete:]
|
|
|
|
|
if n_line.startswith(" " * spaces_to_delete)
|
|
|
|
|
else n_line
|
|
|
|
|
)
|
|
|
|
|
for n_line in c_lines
|
|
|
|
|
]
|
|
|
|
|
filtered_lines = fixed_lines
|
|
|
|
|
filtered_lines = list(
|
|
|
|
|
filter(lambda l: not re.search(r"# \[START .*\]$", l), filtered_lines)
|
|
|
|
|
)
|
|
|
|
|
filtered_lines = list(
|
|
|
|
|
filter(lambda l: not re.search(r"# \[END .*\]$", l), filtered_lines)
|
|
|
|
|
)
|
|
|
|
|
filtered_lines = [
|
|
|
|
|
re.sub(r"app.run\((.*)\)$", r"\1()", s) for s in filtered_lines
|
|
|
|
|
]
|
2025-02-04 18:03:49 +01:00
|
|
|
FULL_TEXT += "\n".join(filtered_lines) + "\n"
|
2024-08-12 13:05:21 +02:00
|
|
|
# Others
|
|
|
|
|
else:
|
|
|
|
|
print("Appending block...")
|
|
|
|
|
filtered_lines = lines[s:e]
|
|
|
|
|
for i, line in enumerate(filtered_lines):
|
|
|
|
|
filtered_lines[i] = line.replace("DEFINE_", "define_")
|
|
|
|
|
filtered_lines = list(
|
|
|
|
|
filter(lambda l: not re.search(r"# \[START .*\]$", l), filtered_lines)
|
|
|
|
|
)
|
|
|
|
|
filtered_lines = list(
|
|
|
|
|
filter(lambda l: not re.search(r"# \[END .*\]$", l), filtered_lines)
|
|
|
|
|
)
|
2025-02-04 18:03:49 +01:00
|
|
|
FULL_TEXT += "\n".join(filtered_lines) + "\n"
|
2024-08-12 13:05:21 +02:00
|
|
|
|
2025-02-04 18:03:49 +01:00
|
|
|
nbook["cells"].append(
|
|
|
|
|
v4.new_code_cell(source=FULL_TEXT, id="code")
|
|
|
|
|
)
|
2024-08-12 13:05:21 +02:00
|
|
|
|
|
|
|
|
jsonform = v4.writes(nbook) + "\n"
|
|
|
|
|
|
|
|
|
|
print(f"writing {output_file}")
|
2025-02-04 18:03:49 +01:00
|
|
|
with open(output_file, mode="w", encoding="utf-8") as fpout:
|
2024-08-12 13:05:21 +02:00
|
|
|
fpout.write(jsonform)
|