Skip to content

Commit

Permalink
Merge pull request #8558 from RasaHQ/mitigate-matplotlib-compat-issue
Browse files Browse the repository at this point in the history
Fix matplotlib backend in decorator
  • Loading branch information
m-vdb authored Apr 28, 2021
2 parents b979224 + 116be04 commit d246d13
Show file tree
Hide file tree
Showing 2 changed files with 38 additions and 6 deletions.
2 changes: 2 additions & 0 deletions changelog/8545.bugfix.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Mitigated Matplotlib backend issue using lazy configuration
and added a more explicit error message to guide users.
42 changes: 36 additions & 6 deletions rasa/utils/plotting.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import logging
import itertools
import os
from functools import wraps

import numpy as np
from typing import List, Text, Optional, Union, Any
from typing import Any, Callable, List, Optional, Text, TypeVar, Union
import matplotlib
from matplotlib.ticker import FormatStrFormatter

Expand All @@ -14,10 +15,20 @@


def _fix_matplotlib_backend() -> None:
"""Tries to fix a broken matplotlib backend..."""
"""Tries to fix a broken matplotlib backend."""
try:
backend = matplotlib.get_backend()
except Exception: # skipcq:PYL-W0703
logger.error(
"Cannot retrieve Matplotlib backend, likely due to a compatibility "
"issue with system dependencies. Please refer to the documentation: "
"https://matplotlib.org/stable/tutorials/introductory/usage.html#backends"
)
raise

# At first, matplotlib will be initialized with default OS-specific
# available backend
if matplotlib.get_backend() == "TkAgg":
if backend == "TkAgg":
try:
# on OSX sometimes the tkinter package is broken and can't be imported.
# we'll try to import it and if it fails we will use a different backend
Expand All @@ -27,7 +38,7 @@ def _fix_matplotlib_backend() -> None:
matplotlib.use("agg")

# if no backend is set by default, we'll try to set it up manually
elif matplotlib.get_backend() is None: # pragma: no cover
elif backend is None: # pragma: no cover
try:
# If the `tkinter` package is available, we can use the `TkAgg` backend
import tkinter # skipcq: PYL-W0611
Expand All @@ -39,10 +50,27 @@ def _fix_matplotlib_backend() -> None:
matplotlib.use("agg")


# we call the fix as soon as this package gets imported
_fix_matplotlib_backend()
ReturnType = TypeVar("ReturnType")
FuncType = Callable[..., ReturnType]
_MATPLOTLIB_BACKEND_FIXED = False


def _needs_matplotlib_backend(func: FuncType) -> FuncType:
"""Decorator to fix matplotlib backend before calling a function."""

@wraps(func)
def inner(*args: Any, **kwargs: Any) -> ReturnType:
"""Replacement function that fixes matplotlib backend."""
global _MATPLOTLIB_BACKEND_FIXED
if not _MATPLOTLIB_BACKEND_FIXED:
_fix_matplotlib_backend()
_MATPLOTLIB_BACKEND_FIXED = True
return func(*args, **kwargs)

return inner


@_needs_matplotlib_backend
def plot_confusion_matrix(
confusion_matrix: np.ndarray,
classes: Union[np.ndarray, List[Text]],
Expand Down Expand Up @@ -117,6 +145,7 @@ def plot_confusion_matrix(
fig.savefig(output_file, bbox_inches="tight")


@_needs_matplotlib_backend
def plot_histogram(
hist_data: List[List[float]], title: Text, output_file: Optional[Text] = None
) -> None:
Expand Down Expand Up @@ -217,6 +246,7 @@ def plot_histogram(
fig.savefig(output_file, bbox_inches="tight")


@_needs_matplotlib_backend
def plot_curve(
output_directory: Text,
number_of_examples: List[int],
Expand Down

0 comments on commit d246d13

Please sign in to comment.