def check_plot_environment(*, print_status: bool = True, apply_style: bool = True) -> Dict[str, Any]:
"""Print and return GRADIEND plot rendering environment status.
When ``apply_style=True``, calls :func:`configure_plot_style` with
:meth:`PlotStyleConfig.from_env`.
"""
env_config: PlotStyleConfig
latex_preference_error = None
try:
env_config = PlotStyleConfig.from_env()
except ValueError as exc:
env_config = PlotStyleConfig()
latex_preference_error = str(exc)
command_paths = _latex_command_paths()
latex_on_path = any(path for path in command_paths.values())
latex_usable = _latex_usable() if latex_on_path else False
resolved_usetex = _resolve_use_latex(env_config) if latex_preference_error is None else False
font_status = _check_configured_font(env_config.font_path)
matplotlib_status: Dict[str, Any] = {
"available": False,
"version": None,
"current_text_usetex": None,
"style_configured": _CONFIGURED,
"current_font_family": None,
"current_font_sans_serif": None,
"error": None,
}
def _refresh_matplotlib_status() -> None:
try:
import matplotlib as mpl
matplotlib_status.update(
{
"available": True,
"version": getattr(mpl, "__version__", None),
"current_text_usetex": bool(mpl.rcParams.get("text.usetex")),
"style_configured": _CONFIGURED,
"current_font_family": mpl.rcParams.get("font.family"),
"current_font_sans_serif": mpl.rcParams.get("font.sans-serif"),
"error": None,
}
)
except Exception as exc:
matplotlib_status["error"] = str(exc)
_refresh_matplotlib_status()
style_status: Optional[PlotStyleStatus] = None
if apply_style and matplotlib_status["available"] and latex_preference_error is None:
try:
style_status = configure_plot_style(env_config, force=False)
_refresh_matplotlib_status()
except Exception as exc:
font_status["usable"] = False
font_status["error"] = str(exc)
_refresh_matplotlib_status()
current_font_status = _check_current_font() if matplotlib_status["available"] else {
"family": None,
"available": False,
"resolved_path": None,
"error": "matplotlib is not available.",
}
font_status["current"] = current_font_status
pref = env_config.use_latex
forced_latex_but_unusable = pref is True and not latex_usable
active_usetex = matplotlib_status["current_text_usetex"]
usetex_mismatch = matplotlib_status["available"] and active_usetex != resolved_usetex
active_family = current_font_status.get("family")
configured_font_name = font_status.get("font_name")
font_style_not_applied = bool(
font_status["usable"]
and configured_font_name
and not apply_style
and not matplotlib_status["style_configured"]
)
expected_font_missing = bool(
font_status["usable"]
and configured_font_name
and matplotlib_status["style_configured"]
and configured_font_name not in (
active_family if isinstance(active_family, (list, tuple)) else [active_family]
)
)
font_unavailable = matplotlib_status["available"] and not current_font_status["available"]
ok = (
latex_preference_error is None
and matplotlib_status["available"]
and font_status["usable"]
and not font_unavailable
and not expected_font_missing
and not forced_latex_but_unusable
and not (usetex_mismatch and apply_style)
)
warnings_list = []
info = []
if latex_preference_error is not None:
warnings_list.append(latex_preference_error)
if forced_latex_but_unusable:
warnings_list.append(f"{ENV_USE_LATEX}=true but LaTeX is not usable by matplotlib.")
if env_config.font_path and str(env_config.font_path).strip() and not font_status["usable"]:
warnings_list.append(str(font_status["error"]))
if font_unavailable:
warnings_list.append(
"Matplotlib cannot resolve the current font.family "
f"({_font_family_text(current_font_status['family'])}): {current_font_status['error']}"
)
if font_style_not_applied:
info.append(
"GRADIEND style has not been applied in this process yet; "
f"plots will register {ENV_FONT_PATH} when rendering."
)
if expected_font_missing:
warnings_list.append(
f"{ENV_FONT_PATH} resolved to {configured_font_name!r}, but matplotlib is currently "
f"using {_font_family_text(active_family)!r}."
)
if not matplotlib_status["available"]:
warnings_list.append(f"matplotlib is not available: {matplotlib_status['error']}")
if usetex_mismatch:
if not apply_style and not matplotlib_status["style_configured"]:
info.append(
"GRADIEND style has not been applied in this process yet; "
f"plots will set text.usetex={resolved_usetex} when rendering."
)
else:
warnings_list.append(
"Matplotlib text.usetex does not match the GRADIEND LaTeX preference "
f"(current={active_usetex}, resolved={resolved_usetex})."
)
latex_pref_label = (
"force_on"
if pref is True
else "force_off"
if pref is False
else "auto"
)
status = {
"ok": ok,
"warnings": warnings_list,
"info": info,
"style": {
"transition_arrows": env_config.transition_arrows,
"font_family": env_config.font_family,
"font_family_env_var": ENV_FONT_FAMILY,
"font_family_env_value": os.environ.get(ENV_FONT_FAMILY),
"latex_preamble_extra": bool((env_config.latex_preamble_extra or "").strip()),
"configured": style_status.configured if style_status else _CONFIGURED,
"text_usetex": style_status.text_usetex if style_status else active_usetex,
},
"latex": {
"env_var": ENV_USE_LATEX,
"env_value": os.environ.get(ENV_USE_LATEX),
"preference": latex_pref_label,
"preference_error": latex_preference_error,
"commands": command_paths,
"on_path": latex_on_path,
"usable": latex_usable,
"resolved_text_usetex": resolved_usetex,
},
"font": {
"env_var": ENV_FONT_PATH,
"family_env_var": ENV_FONT_FAMILY,
"family_env_value": os.environ.get(ENV_FONT_FAMILY),
**font_status,
},
"matplotlib": matplotlib_status,
}
if print_status:
print(_format_plot_environment_status(status))
return status