Skip to content
Closed
Show file tree
Hide file tree
Changes from all 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
23 changes: 12 additions & 11 deletions tests/utils_for_qmod.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,28 +2,29 @@
import os
from pathlib import Path
from typing import Any, Callable
import sys


def qmod_compare_decorator(func: Callable) -> Any:
def inner(*args: Any, **kwargs: Any) -> Any:
qmods_before = _read_qmod_files()

# collect the error of the test itself, in case of such error
all_errors = []
try:
result = func(*args, **kwargs)
except Exception as exc:
all_errors.append(str(exc))
finally:
qmods_after = _read_qmod_files()

qmods_after = _read_qmod_files()
# collect the errors of the qmod comparison, in case of such errors
# prepend all the errors from `compare_qmods`, so that `actions/send_qmod_slack_notification` will have a simpler regex
all_errors = _compare_qmods(qmods_before, qmods_after) + all_errors
all_errors = _compare_qmods(qmods_before, qmods_after)

# raise all errors
assert not all_errors, "\n".join(all_errors)
# intentionally not collection the exception in an `except` block
# this way, in case `_compare_qmods` raised errors, we will print all the errors
# plus have the full traceback of the error from `func`
exc_type, exc_value, exc_tb = sys.exc_info()
if exc_type is not None:
# prepend all the errors from `compare_qmods`, so that `actions/send_qmod_slack_notification` will have a simpler regex
all_errors += [f"{exc_type.__name__}({exc_value})"]

return result
assert not all_errors, "\n".join(all_errors)

return inner

Expand Down
24 changes: 24 additions & 0 deletions tests/utils_for_testbook.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import json
from nbclient.exceptions import CellExecutionError
import warnings
import itertools
import base64
Expand Down Expand Up @@ -58,6 +59,7 @@ def inner_decorator(func: Callable) -> Any:
for decorator in [
_build_patch_testbook_client_decorator(notebook_name),
testbook(notebook_path, execute=True, timeout=timeout_seconds),
maybe_truncate_notebook_errors,
qmod_compare_decorator,
_build_cd_decorator(notebook_path),
_build_skip_decorator(notebook_path),
Expand All @@ -82,6 +84,28 @@ def inner(*args: Any, **kwargs: Any) -> Any:
return patch_testbook_client_decorator


class ShortCellError(Exception):
def __init__(self, exc: CellExecutionError):
self.ename = exc.ename
self.evalue = exc.evalue

def __str__(self) -> str:
return f'A cell raised: {self.ename}("{self.evalue}")'


def maybe_truncate_notebook_errors(func: Callable) -> Any:
def inner(*args: Any, **kwargs: Any) -> Any:
try:
return func(*args, **kwargs)
except CellExecutionError as exc:
if os.environ.get("SHOULD_TRUNCATE_SHORT_CELL_ERROR", "") == "true":
raise ShortCellError(exc) from exc
else:
raise

return inner


# The purpose of the `cd_decorator` is to execute the test in the same folder as the `ipynb` file
# so that relative files (images, csv, etc.) will be available
def _build_cd_decorator(file_path: str) -> Callable:
Expand Down