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..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
@@ -2,6 +2,8 @@
from __future__ import annotations
+import contextlib
+import re
import warnings
from typing import TYPE_CHECKING, Any, Optional, Union
@@ -10,12 +12,56 @@
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
+# use variables for HTML warnings
+# pick vscode color then fallback to jupyter color
+# then fallback to orange
+HTML_WARNING_TEMPLATE = """
+
+
+
{warning_class}
+
{warning}
+
+"""
+
+
def train_test_split(
*arrays: ArrayLike,
X: Optional[ArrayLike] = None,
@@ -158,10 +204,36 @@ 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(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))
+ 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"]