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

Run all Python doc examples in CI #3172

Merged
merged 7 commits into from
Aug 31, 2023
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
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
2 changes: 1 addition & 1 deletion crates/re_types/source_hash.txt

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions docs/code-examples/annotation_context_connections.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
rr.log_annotation_context(
"/",
rr.ClassDescription(
info=0,
keypoint_annotations=[
rr.AnnotationInfo(0, "zero", (255, 0, 0)),
rr.AnnotationInfo(1, "one", (0, 255, 0)),
Expand Down
3 changes: 2 additions & 1 deletion docs/code-examples/transform3d_simple_v2.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@
rr2.log(
"base/rotated_scaled",
rrd.TranslationRotationScale3D(
rotation=rrd.RotationAxisAngle(axis=[0, 0, 1], radians=3.14 / 4), scale=rrd.Scale3D(2)
rotation=rrd.RotationAxisAngle(axis=[0, 0, 1], angle=rrd.Angle(rad=3.14 / 4)),
scale=2,
),
)

Expand Down
3 changes: 2 additions & 1 deletion rerun_py/rerun_sdk/rerun/_rerun2/archetypes/transform3d.py

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

50 changes: 46 additions & 4 deletions scripts/run_python_e2e_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,16 @@
from __future__ import annotations

import argparse
import fileinput
import os
import shutil
import subprocess
import sys
import tempfile
import time
from typing import Iterable

PORT = 9752


def main() -> None:
Expand Down Expand Up @@ -67,6 +73,7 @@ def main() -> None:
("examples/python/plots/main.py", []),
("examples/python/text_logging/main.py", []),
]

for example, args in examples:
print("----------------------------------------------------------")
print(f"Testing {example}…\n")
Expand All @@ -76,24 +83,37 @@ def main() -> None:
print(f"{example} done in {elapsed:.1f} seconds")
print()

# NOTE: Doc-examples don't take any parameters and we want to keep it that way.
# For that reason, we copy them to a temporary directory and monkey patch them so
# that they connect to a remote Rerun viewer rather than spawn()ing.
DOC_EXAMPLES_DIR_PATH = "docs/code-examples/"
old_str = ", spawn=True)"
new_str = f'); rr.connect(addr="127.0.0.1:{PORT}");'
for original, example in copy_and_patch(DOC_EXAMPLES_DIR_PATH, old_str, new_str):
print("----------------------------------------------------------")
print(f"Testing {original}…\n")
start_time = time.time()
run_example(example, [])
elapsed = time.time() - start_time
print(f"{original} done in {elapsed:.1f} seconds")
print()

print()
print("All tests passed successfully!")


def run_example(example: str, args: list[str]) -> None:
port = 9752

# sys.executable: the absolute path of the executable binary for the Python interpreter
python_executable = sys.executable
if python_executable is None:
python_executable = "python3"

rerun_process = subprocess.Popen(
[python_executable, "-m", "rerun", "--port", str(port), "--strict", "--test-receive"]
[python_executable, "-m", "rerun", "--port", str(PORT), "--strict", "--test-receive"]
)
time.sleep(0.3) # Wait for rerun server to start to remove a logged warning

python_process = subprocess.Popen([python_executable, example, "--connect", "--addr", f"127.0.0.1:{port}"] + args)
python_process = subprocess.Popen([python_executable, example, "--connect", "--addr", f"127.0.0.1:{PORT}"] + args)

print("Waiting for python process to finish…")
returncode = python_process.wait(timeout=30)
Expand All @@ -104,5 +124,27 @@ def run_example(example: str, args: list[str]) -> None:
assert returncode == 0, f"rerun process exited with error code {returncode}"


# Copies all files in a directory to a tempdir and replaces `old_str` with `new_str` in-place.
#
# Yields the patched filenames as it goes.
# When all file have been yielded, the temporary directory is destroyed.
def copy_and_patch(src_dir: str, old_str: str, new_str: str) -> Iterable[tuple[str, str]]:
with tempfile.TemporaryDirectory() as tmp_dir:
for root, _, files in os.walk(src_dir):
for file in [f for f in files if f.endswith(".py")]:
src_path = os.path.join(root, file)
dest_path = os.path.join(tmp_dir, file)
shutil.copy(src_path, dest_path)

with fileinput.FileInput(dest_path, inplace=True) as f:
for line in f:
print(line.replace(old_str, new_str), end="")

print(src_path)
teh-cmc marked this conversation as resolved.
Show resolved Hide resolved
yield (src_path, dest_path)

break # NOTE: Do _not_ recurse into sub-dirs, only weird non-runnable examples live there.


if __name__ == "__main__":
main()