From 7490f19f046e5c4371f757fac4d7632ce9343a4d Mon Sep 17 00:00:00 2001 From: Matt J Date: Wed, 8 Jan 2025 19:39:26 +0100 Subject: [PATCH 1/4] feat: Use HTML to present warnings of train_test_split when in vscode or jupyter --- .../train_test_split/train_test_split.py | 37 +++++++++++++ skore/src/skore/utils/_environment.py | 52 +++++++++++++++++++ 2 files changed, 89 insertions(+) create mode 100644 skore/src/skore/utils/_environment.py diff --git a/skore/src/skore/sklearn/train_test_split/train_test_split.py b/skore/src/skore/sklearn/train_test_split/train_test_split.py index 48b54bb54d..55a7d9a113 100644 --- a/skore/src/skore/sklearn/train_test_split/train_test_split.py +++ b/skore/src/skore/sklearn/train_test_split/train_test_split.py @@ -2,6 +2,7 @@ from __future__ import annotations +import contextlib import warnings from typing import TYPE_CHECKING, Any, Optional, Union @@ -10,12 +11,29 @@ from skore.sklearn.find_ml_task import _find_ml_task from skore.sklearn.train_test_split.warning import TRAIN_TEST_SPLIT_WARNINGS +from skore.utils._environment import is_environment_html_capable if TYPE_CHECKING: ArrayLike = Any from skore.project import Project +HTML_WARNING_TEMPLATE = """ +
+ {warning} +
+""" + + def train_test_split( *arrays: ArrayLike, X: Optional[ArrayLike] = None, @@ -158,10 +176,29 @@ class labels. ml_task=ml_task, ) + to_display = [] for warning_class in TRAIN_TEST_SPLIT_WARNINGS: warning = warning_class.check(**kwargs) if warning is not None: + to_display.append((warning, warning_class)) + + if is_environment_html_capable(): + with contextlib.suppress(ImportError): + from IPython.core.interactiveshell import InteractiveShell + from IPython.display import HTML, display + from markdown import markdown + + if InteractiveShell.initialized(): + markup = "".join( + [ + HTML_WARNING_TEMPLATE.format(warning=markdown(w)) + for w, _ in to_display + ] + ) + display(HTML(markup)) + else: + for warning, warning_class in to_display: warnings.warn(message=warning, category=warning_class, stacklevel=1) return output diff --git a/skore/src/skore/utils/_environment.py b/skore/src/skore/utils/_environment.py new file mode 100644 index 0000000000..aff9e6f6b7 --- /dev/null +++ b/skore/src/skore/utils/_environment.py @@ -0,0 +1,52 @@ +import os +import sys + + +def get_environment_info(): + """Detect the current Python execution environment. + + Returns a dictionary with information about the environment. + """ + env_info = { + "is_jupyter": False, + "is_vscode": False, + "is_interactive": False, + "environment_name": "standard_python", + "details": {}, + } + + # Check for interactive mode + env_info["is_interactive"] = hasattr(sys, "ps1") + + # Check for Jupyter + try: + shell = get_ipython().__class__.__name__ # type: ignore + env_info["details"]["ipython_shell"] = shell + + if shell == "ZMQInteractiveShell": # Jupyter notebook/lab + env_info["is_jupyter"] = True + env_info["environment_name"] = "jupyter" + elif shell == "TerminalInteractiveShell": # IPython terminal + env_info["environment_name"] = "ipython_terminal" + except NameError: + pass + + # Check for VSCode + if "VSCODE_PID" in os.environ: + env_info["is_vscode"] = True + if env_info["is_interactive"]: + env_info["environment_name"] = "vscode_interactive" + else: + env_info["environment_name"] = "vscode_script" + + # Add additional environment details + env_info["details"]["python_executable"] = sys.executable + env_info["details"]["python_version"] = sys.version + + return env_info + + +def is_environment_html_capable() -> bool: + """Return `True` if the execution context dcan render HTML. `False` otherwise.""" + info = get_environment_info() + return info["is_vscode"] or info["is_jupyter"] From cb201474d6fc69c5e1e2a51c004778d4d3de9b66 Mon Sep 17 00:00:00 2001 From: Matt J Date: Thu, 9 Jan 2025 11:20:24 +0100 Subject: [PATCH 2/4] use environement css vars as much as possible --- .../train_test_split/train_test_split.py | 29 +++++++++++++++---- 1 file changed, 23 insertions(+), 6 deletions(-) diff --git a/skore/src/skore/sklearn/train_test_split/train_test_split.py b/skore/src/skore/sklearn/train_test_split/train_test_split.py index 55a7d9a113..c0ab0fc29b 100644 --- a/skore/src/skore/sklearn/train_test_split/train_test_split.py +++ b/skore/src/skore/sklearn/train_test_split/train_test_split.py @@ -18,17 +18,34 @@ from skore.project import Project +# use variables for HTML warnings +# pick vscode color then fallback to jupyter color +# then fallback to orange HTML_WARNING_TEMPLATE = """ -
+}} +.jp-RenderedHTMLCommon .warning p {{ + margin-bottom: 0; +}} + +
{warning}
""" From 9e94136f83131381706db630e6f3e83036ac5ef4 Mon Sep 17 00:00:00 2001 From: Matt J Date: Thu, 9 Jan 2025 18:11:02 +0100 Subject: [PATCH 3/4] add warning title --- .../train_test_split/train_test_split.py | 28 +++++++++++++++---- 1 file changed, 23 insertions(+), 5 deletions(-) diff --git a/skore/src/skore/sklearn/train_test_split/train_test_split.py b/skore/src/skore/sklearn/train_test_split/train_test_split.py index c0ab0fc29b..4d1b445808 100644 --- a/skore/src/skore/sklearn/train_test_split/train_test_split.py +++ b/skore/src/skore/sklearn/train_test_split/train_test_split.py @@ -3,6 +3,7 @@ from __future__ import annotations import contextlib +import re import warnings from typing import TYPE_CHECKING, Any, Optional, Union @@ -38,15 +39,25 @@ background-color: transparent; color: var(--skore-warn-color); font-family: inherit; - font-size: 1em; - text-wrap: balance; + + .title {{ + font-size: 1.2em; + font-weight: bold; + margin: 1em 0; + }} + + .content {{ + font-size: 1em; + text-wrap: balance; + }} }} .jp-RenderedHTMLCommon .warning p {{ margin-bottom: 0; }}
- {warning} +
{warning_class}
+
{warning}
""" @@ -209,8 +220,15 @@ class labels. if InteractiveShell.initialized(): markup = "".join( [ - HTML_WARNING_TEMPLATE.format(warning=markdown(w)) - for w, _ in to_display + HTML_WARNING_TEMPLATE.format( + warning=markdown(warning), + warning_class=re.sub( + "([A-Z][a-z]+)", + r" \1", + re.sub("([A-Z]+)", r" \1", warning_class.__name__), + ).lstrip(), + ) + for warning, warning_class in to_display ] ) display(HTML(markup)) From 2195aad24f4421cecbb92de486298e6f046d0528 Mon Sep 17 00:00:00 2001 From: Matt J Date: Fri, 10 Jan 2025 10:34:31 +0100 Subject: [PATCH 4/4] fix bottom padding in jupyter --- skore/src/skore/sklearn/train_test_split/train_test_split.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/skore/src/skore/sklearn/train_test_split/train_test_split.py b/skore/src/skore/sklearn/train_test_split/train_test_split.py index 4d1b445808..11ccbfd26b 100644 --- a/skore/src/skore/sklearn/train_test_split/train_test_split.py +++ b/skore/src/skore/sklearn/train_test_split/train_test_split.py @@ -51,8 +51,8 @@ text-wrap: balance; }} }} -.jp-RenderedHTMLCommon .warning p {{ - margin-bottom: 0; +.jp-RenderedHTMLCommon .warning .content p {{ + margin-bottom: 1em; }}