We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
user-provided matplotlib backend names list causes TypeError: unhashable type: 'list'
## Description of the issue

* Output of `pyinstaller --version`: ```5.4.1```
* Version of Python: 3.7.9
* Platform: Windows 10

When implementing the matplotliib backend hook configuration example in the docs:

```
a = Analysis(
    ["my-matplotlib-app.py"],
    ...,
    hooksconfig={
        "matplotlib": {
            #"backends": "auto",  # auto-detect; the default behavior
            # "backends": "all",  # collect all backends
            # "backends": "TkAgg",  # collect a specific backend
            "backends": ["TkAgg", "Qt5Agg"],  # collect multiple backends
        },
    },
    ...,
)
```

I get the following exception:

```
  File "C:\Users\...\lib\site-packages\PyInstaller\hooks\hook-matplotlib.backends.py", line 192, in hook
    logger.info("Matplotlib backend selection method: %s", _method_names.get(backends_method, 'user-provided name(s)'))
TypeError: unhashable type: 'list'

```
I think perhaps this is just an issue with the logger.info call?

I'm very inexperienced with GitHub sorry, otherwise I would try to submit a PR.



--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `PyInstaller/hooks/hook-matplotlib.backends.py`
Content:
```
1 #-----------------------------------------------------------------------------
2 # Copyright (c) 2013-2022, PyInstaller Development Team.
3 #
4 # Distributed under the terms of the GNU General Public License (version 2
5 # or later) with exception for distributing the bootloader.
6 #
7 # The full license is in the file COPYING.txt, distributed with this software.
8 #
9 # SPDX-License-Identifier: (GPL-2.0-or-later WITH Bootloader-exception)
10 #-----------------------------------------------------------------------------
11 
12 from PyInstaller.compat import is_darwin
13 from PyInstaller.utils.hooks import logger, get_hook_config
14 from PyInstaller import isolated
15 
16 
17 @isolated.decorate
18 def _get_configured_default_backend():
19     """
20     Return the configured default matplotlib backend name, if available as matplotlib.rcParams['backend'] (or overridden
21     by MPLBACKEND environment variable. If the value of matplotlib.rcParams['backend'] corresponds to the auto-sentinel
22     object, returns None
23     """
24     import matplotlib
25     # matplotlib.rcParams overrides the __getitem__ implementation and attempts to determine and load the default
26     # backend using pyplot.switch_backend(). Therefore, use dict.__getitem__().
27     val = dict.__getitem__(matplotlib.rcParams, 'backend')
28     if isinstance(val, str):
29         return val
30     return None
31 
32 
33 @isolated.decorate
34 def _list_available_mpl_backends():
35     """
36     Returns the names of all available matplotlib backends.
37     """
38     import matplotlib
39     return matplotlib.rcsetup.all_backends
40 
41 
42 @isolated.decorate
43 def _check_mpl_backend_importable(module_name):
44     """
45     Attempts to import the given module name (matplotlib backend module).
46 
47     Exceptions are propagated to caller.
48     """
49     __import__(module_name)
50 
51 
52 # Bytecode scanning
53 def _recursive_scan_code_objects_for_mpl_use(co):
54     """
55     Recursively scan the bytecode for occurrences of matplotlib.use() or mpl.use() calls with const arguments, and
56     collect those arguments into list of used matplotlib backend names.
57     """
58 
59     from PyInstaller.depend.bytecode import any_alias, recursive_function_calls
60 
61     mpl_use_names = {
62         *any_alias("matplotlib.use"),
63         *any_alias("mpl.use"),  # matplotlib is commonly aliased as mpl
64     }
65 
66     backends = []
67     for calls in recursive_function_calls(co).values():
68         for name, args in calls:
69             # matplotlib.use(backend) or matplotlib.use(backend, force)
70             # We support only literal arguments. Similarly, kwargs are
71             # not supported.
72             if not len(args) in {1, 2} or not isinstance(args[0], str):
73                 continue
74             if name in mpl_use_names:
75                 backends.append(args[0])
76 
77     return backends
78 
79 
80 def _backend_module_name(name):
81     """
82     Converts matplotlib backend name to its corresponding module name.
83 
84     Equivalent to matplotlib.cbook._backend_module_name().
85     """
86     if name.startswith("module://"):
87         return name[9:]
88     return f"matplotlib.backends.backend_{name.lower()}"
89 
90 
91 def _autodetect_used_backends(hook_api):
92     """
93     Returns a list of automatically-discovered matplotlib backends in use, or the name of the default matplotlib
94     backend. Implements the 'auto' backend selection method.
95     """
96     # Scan the code for matplotlib.use()
97     modulegraph = hook_api.analysis.graph
98     mpl_code_objs = modulegraph.get_code_using("matplotlib")
99     used_backends = []
100     for name, co in mpl_code_objs.items():
101         used_backends += _recursive_scan_code_objects_for_mpl_use(co)
102 
103     if used_backends:
104         return used_backends
105 
106     # Determine the default matplotlib backend.
107     #
108     # Ideally, this would be done by calling ``matplotlib.get_backend()``. However, that function tries to switch to the
109     # default backend (calling ``matplotlib.pyplot.switch_backend()``), which seems to occasionally fail on our linux CI
110     # with an error and, on other occasions, returns the headless Agg backend instead of the GUI one (even with display
111     # server running). Furthermore, using ``matplotlib.get_backend()`` returns headless 'Agg' when display server is
112     # unavailable, which is not ideal for automated builds.
113     #
114     # Therefore, we try to emulate ``matplotlib.get_backend()`` ourselves. First, we try to obtain the configured
115     # default backend from settings (rcparams and/or MPLBACKEND environment variable). If that is unavailable, we try to
116     # find the first importable GUI-based backend, using the same list as matplotlib.pyplot.switch_backend() uses for
117     # automatic backend selection. The difference is that we only test whether the backend module is importable, without
118     # trying to switch to it.
119     default_backend = _get_configured_default_backend()  # isolated sub-process
120     if default_backend:
121         logger.info("Found configured default matplotlib backend: %s", default_backend)
122         return [default_backend]
123 
124     candidates = ["Qt5Agg", "Gtk3Agg", "TkAgg", "WxAgg"]
125     if is_darwin:
126         candidates = ["MacOSX"] + candidates
127     logger.info("Trying determine the default backend as first importable candidate from the list: %r", candidates)
128 
129     for candidate in candidates:
130         try:
131             module_name = _backend_module_name(candidate)
132             _check_mpl_backend_importable(module_name)  # NOTE: uses an isolated sub-process.
133         except Exception:
134             continue
135         return [candidate]
136 
137     # Fall back to headless Agg backend
138     logger.info("None of the backend candidates could be imported; falling back to headless Agg!")
139     return ['Agg']
140 
141 
142 def _collect_all_importable_backends(hook_api):
143     """
144     Returns a list of all importable matplotlib backends. Implements the 'all' backend selection method.
145     """
146     # List of the human-readable names of all available backends.
147     backend_names = _list_available_mpl_backends()  # NOTE: retrieved in an isolated sub-process.
148     logger.info("All available matplotlib backends: %r", backend_names)
149 
150     # Try to import the module(s).
151     importable_backends = []
152 
153     # List of backends to exclude; Qt4 is not supported by PyInstaller anymore.
154     exclude_backends = {'Qt4Agg', 'Qt4Cairo'}
155 
156     # Ignore "CocoaAgg" on OSes other than Mac OS; attempting to import it on other OSes halts the current
157     # (sub)process without printing output or raising exceptions, preventing reliable detection. Apply the
158     # same logic for the (newer) "MacOSX" backend.
159     if not is_darwin:
160         exclude_backends |= {'CocoaAgg', 'MacOSX'}
161 
162     # For safety, attempt to import each backend in an isolated sub-process.
163     for backend_name in backend_names:
164         if backend_name in exclude_backends:
165             logger.info('  Matplotlib backend %r: excluded', backend_name)
166             continue
167 
168         try:
169             module_name = _backend_module_name(backend_name)
170             _check_mpl_backend_importable(module_name)  # NOTE: uses an isolated sub-process.
171         except Exception:
172             # Backend is not importable, for whatever reason.
173             logger.info('  Matplotlib backend %r: ignored due to import error', backend_name)
174             continue
175 
176         logger.info('  Matplotlib backend %r: added', backend_name)
177         importable_backends.append(backend_name)
178 
179     return importable_backends
180 
181 
182 def hook(hook_api):
183     # Backend collection setting
184     backends_method = get_hook_config(hook_api, 'matplotlib', 'backends')
185     if backends_method is None:
186         backends_method = 'auto'  # default method
187 
188     _method_names = {
189         'auto': 'automatic discovery of used backends',
190         'all': 'collection of all importable backends',
191     }
192     logger.info("Matplotlib backend selection method: %s", _method_names.get(backends_method, 'user-provided name(s)'))
193 
194     # Select backend(s)
195     if backends_method == 'auto':
196         backend_names = _autodetect_used_backends(hook_api)
197     elif backends_method == 'all':
198         backend_names = _collect_all_importable_backends(hook_api)
199     else:
200         if isinstance(backends_method, str):
201             backend_names = [backends_method]
202         else:
203             assert isinstance(backends_method, list), "User-provided backend name(s) must be either a string or a list!"
204             backend_names = backends_method
205 
206     logger.info("Selected matplotlib backends: %r", backend_names)
207 
208     # Set module names as hiddenimports
209     module_names = [_backend_module_name(backend) for backend in backend_names]  # backend name -> module name
210     hook_api.add_imports(*module_names)
211
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/PyInstaller/hooks/hook-matplotlib.backends.py b/PyInstaller/hooks/hook-matplotlib.backends.py
--- a/PyInstaller/hooks/hook-matplotlib.backends.py
+++ b/PyInstaller/hooks/hook-matplotlib.backends.py
@@ -185,18 +185,15 @@
     if backends_method is None:
         backends_method = 'auto'  # default method
 
-    _method_names = {
-        'auto': 'automatic discovery of used backends',
-        'all': 'collection of all importable backends',
-    }
-    logger.info("Matplotlib backend selection method: %s", _method_names.get(backends_method, 'user-provided name(s)'))
-
     # Select backend(s)
     if backends_method == 'auto':
+        logger.info("Matplotlib backend selection method: automatic discovery of used backends")
         backend_names = _autodetect_used_backends(hook_api)
     elif backends_method == 'all':
+        logger.info("Matplotlib backend selection method: collection of all importable backends")
         backend_names = _collect_all_importable_backends(hook_api)
     else:
+        logger.info("Matplotlib backend selection method: user-provided name(s)")
         if isinstance(backends_method, str):
             backend_names = [backends_method]
         else:

{"golden_diff": "diff --git a/PyInstaller/hooks/hook-matplotlib.backends.py b/PyInstaller/hooks/hook-matplotlib.backends.py\n--- a/PyInstaller/hooks/hook-matplotlib.backends.py\n+++ b/PyInstaller/hooks/hook-matplotlib.backends.py\n@@ -185,18 +185,15 @@\n     if backends_method is None:\n         backends_method = 'auto'  # default method\n \n-    _method_names = {\n-        'auto': 'automatic discovery of used backends',\n-        'all': 'collection of all importable backends',\n-    }\n-    logger.info(\"Matplotlib backend selection method: %s\", _method_names.get(backends_method, 'user-provided name(s)'))\n-\n     # Select backend(s)\n     if backends_method == 'auto':\n+        logger.info(\"Matplotlib backend selection method: automatic discovery of used backends\")\n         backend_names = _autodetect_used_backends(hook_api)\n     elif backends_method == 'all':\n+        logger.info(\"Matplotlib backend selection method: collection of all importable backends\")\n         backend_names = _collect_all_importable_backends(hook_api)\n     else:\n+        logger.info(\"Matplotlib backend selection method: user-provided name(s)\")\n         if isinstance(backends_method, str):\n             backend_names = [backends_method]\n         else:\n", "issue": "user-provided matplotlib backend names list causes TypeError: unhashable type: 'list'\n## Description of the issue\r\n\r\n* Output of `pyinstaller --version`: ```5.4.1```\r\n* Version of Python: 3.7.9\r\n* Platform: Windows 10\r\n\r\nWhen implementing the matplotliib backend hook configuration example in the docs:\r\n\r\n```\r\na = Analysis(\r\n    [\"my-matplotlib-app.py\"],\r\n    ...,\r\n    hooksconfig={\r\n        \"matplotlib\": {\r\n            #\"backends\": \"auto\",  # auto-detect; the default behavior\r\n            # \"backends\": \"all\",  # collect all backends\r\n            # \"backends\": \"TkAgg\",  # collect a specific backend\r\n            \"backends\": [\"TkAgg\", \"Qt5Agg\"],  # collect multiple backends\r\n        },\r\n    },\r\n    ...,\r\n)\r\n```\r\n\r\nI get the following exception:\r\n\r\n```\r\n  File \"C:\\Users\\...\\lib\\site-packages\\PyInstaller\\hooks\\hook-matplotlib.backends.py\", line 192, in hook\r\n    logger.info(\"Matplotlib backend selection method: %s\", _method_names.get(backends_method, 'user-provided name(s)'))\r\nTypeError: unhashable type: 'list'\r\n\r\n```\r\nI think perhaps this is just an issue with the logger.info call?\r\n\r\nI'm very inexperienced with GitHub sorry, otherwise I would try to submit a PR.\r\n\r\n\n", "before_files": [{"content": "#-----------------------------------------------------------------------------\n# Copyright (c) 2013-2022, PyInstaller Development Team.\n#\n# Distributed under the terms of the GNU General Public License (version 2\n# or later) with exception for distributing the bootloader.\n#\n# The full license is in the file COPYING.txt, distributed with this software.\n#\n# SPDX-License-Identifier: (GPL-2.0-or-later WITH Bootloader-exception)\n#-----------------------------------------------------------------------------\n\nfrom PyInstaller.compat import is_darwin\nfrom PyInstaller.utils.hooks import logger, get_hook_config\nfrom PyInstaller import isolated\n\n\n@isolated.decorate\ndef _get_configured_default_backend():\n    \"\"\"\n    Return the configured default matplotlib backend name, if available as matplotlib.rcParams['backend'] (or overridden\n    by MPLBACKEND environment variable. If the value of matplotlib.rcParams['backend'] corresponds to the auto-sentinel\n    object, returns None\n    \"\"\"\n    import matplotlib\n    # matplotlib.rcParams overrides the __getitem__ implementation and attempts to determine and load the default\n    # backend using pyplot.switch_backend(). Therefore, use dict.__getitem__().\n    val = dict.__getitem__(matplotlib.rcParams, 'backend')\n    if isinstance(val, str):\n        return val\n    return None\n\n\n@isolated.decorate\ndef _list_available_mpl_backends():\n    \"\"\"\n    Returns the names of all available matplotlib backends.\n    \"\"\"\n    import matplotlib\n    return matplotlib.rcsetup.all_backends\n\n\n@isolated.decorate\ndef _check_mpl_backend_importable(module_name):\n    \"\"\"\n    Attempts to import the given module name (matplotlib backend module).\n\n    Exceptions are propagated to caller.\n    \"\"\"\n    __import__(module_name)\n\n\n# Bytecode scanning\ndef _recursive_scan_code_objects_for_mpl_use(co):\n    \"\"\"\n    Recursively scan the bytecode for occurrences of matplotlib.use() or mpl.use() calls with const arguments, and\n    collect those arguments into list of used matplotlib backend names.\n    \"\"\"\n\n    from PyInstaller.depend.bytecode import any_alias, recursive_function_calls\n\n    mpl_use_names = {\n        *any_alias(\"matplotlib.use\"),\n        *any_alias(\"mpl.use\"),  # matplotlib is commonly aliased as mpl\n    }\n\n    backends = []\n    for calls in recursive_function_calls(co).values():\n        for name, args in calls:\n            # matplotlib.use(backend) or matplotlib.use(backend, force)\n            # We support only literal arguments. Similarly, kwargs are\n            # not supported.\n            if not len(args) in {1, 2} or not isinstance(args[0], str):\n                continue\n            if name in mpl_use_names:\n                backends.append(args[0])\n\n    return backends\n\n\ndef _backend_module_name(name):\n    \"\"\"\n    Converts matplotlib backend name to its corresponding module name.\n\n    Equivalent to matplotlib.cbook._backend_module_name().\n    \"\"\"\n    if name.startswith(\"module://\"):\n        return name[9:]\n    return f\"matplotlib.backends.backend_{name.lower()}\"\n\n\ndef _autodetect_used_backends(hook_api):\n    \"\"\"\n    Returns a list of automatically-discovered matplotlib backends in use, or the name of the default matplotlib\n    backend. Implements the 'auto' backend selection method.\n    \"\"\"\n    # Scan the code for matplotlib.use()\n    modulegraph = hook_api.analysis.graph\n    mpl_code_objs = modulegraph.get_code_using(\"matplotlib\")\n    used_backends = []\n    for name, co in mpl_code_objs.items():\n        used_backends += _recursive_scan_code_objects_for_mpl_use(co)\n\n    if used_backends:\n        return used_backends\n\n    # Determine the default matplotlib backend.\n    #\n    # Ideally, this would be done by calling ``matplotlib.get_backend()``. However, that function tries to switch to the\n    # default backend (calling ``matplotlib.pyplot.switch_backend()``), which seems to occasionally fail on our linux CI\n    # with an error and, on other occasions, returns the headless Agg backend instead of the GUI one (even with display\n    # server running). Furthermore, using ``matplotlib.get_backend()`` returns headless 'Agg' when display server is\n    # unavailable, which is not ideal for automated builds.\n    #\n    # Therefore, we try to emulate ``matplotlib.get_backend()`` ourselves. First, we try to obtain the configured\n    # default backend from settings (rcparams and/or MPLBACKEND environment variable). If that is unavailable, we try to\n    # find the first importable GUI-based backend, using the same list as matplotlib.pyplot.switch_backend() uses for\n    # automatic backend selection. The difference is that we only test whether the backend module is importable, without\n    # trying to switch to it.\n    default_backend = _get_configured_default_backend()  # isolated sub-process\n    if default_backend:\n        logger.info(\"Found configured default matplotlib backend: %s\", default_backend)\n        return [default_backend]\n\n    candidates = [\"Qt5Agg\", \"Gtk3Agg\", \"TkAgg\", \"WxAgg\"]\n    if is_darwin:\n        candidates = [\"MacOSX\"] + candidates\n    logger.info(\"Trying determine the default backend as first importable candidate from the list: %r\", candidates)\n\n    for candidate in candidates:\n        try:\n            module_name = _backend_module_name(candidate)\n            _check_mpl_backend_importable(module_name)  # NOTE: uses an isolated sub-process.\n        except Exception:\n            continue\n        return [candidate]\n\n    # Fall back to headless Agg backend\n    logger.info(\"None of the backend candidates could be imported; falling back to headless Agg!\")\n    return ['Agg']\n\n\ndef _collect_all_importable_backends(hook_api):\n    \"\"\"\n    Returns a list of all importable matplotlib backends. Implements the 'all' backend selection method.\n    \"\"\"\n    # List of the human-readable names of all available backends.\n    backend_names = _list_available_mpl_backends()  # NOTE: retrieved in an isolated sub-process.\n    logger.info(\"All available matplotlib backends: %r\", backend_names)\n\n    # Try to import the module(s).\n    importable_backends = []\n\n    # List of backends to exclude; Qt4 is not supported by PyInstaller anymore.\n    exclude_backends = {'Qt4Agg', 'Qt4Cairo'}\n\n    # Ignore \"CocoaAgg\" on OSes other than Mac OS; attempting to import it on other OSes halts the current\n    # (sub)process without printing output or raising exceptions, preventing reliable detection. Apply the\n    # same logic for the (newer) \"MacOSX\" backend.\n    if not is_darwin:\n        exclude_backends |= {'CocoaAgg', 'MacOSX'}\n\n    # For safety, attempt to import each backend in an isolated sub-process.\n    for backend_name in backend_names:\n        if backend_name in exclude_backends:\n            logger.info('  Matplotlib backend %r: excluded', backend_name)\n            continue\n\n        try:\n            module_name = _backend_module_name(backend_name)\n            _check_mpl_backend_importable(module_name)  # NOTE: uses an isolated sub-process.\n        except Exception:\n            # Backend is not importable, for whatever reason.\n            logger.info('  Matplotlib backend %r: ignored due to import error', backend_name)\n            continue\n\n        logger.info('  Matplotlib backend %r: added', backend_name)\n        importable_backends.append(backend_name)\n\n    return importable_backends\n\n\ndef hook(hook_api):\n    # Backend collection setting\n    backends_method = get_hook_config(hook_api, 'matplotlib', 'backends')\n    if backends_method is None:\n        backends_method = 'auto'  # default method\n\n    _method_names = {\n        'auto': 'automatic discovery of used backends',\n        'all': 'collection of all importable backends',\n    }\n    logger.info(\"Matplotlib backend selection method: %s\", _method_names.get(backends_method, 'user-provided name(s)'))\n\n    # Select backend(s)\n    if backends_method == 'auto':\n        backend_names = _autodetect_used_backends(hook_api)\n    elif backends_method == 'all':\n        backend_names = _collect_all_importable_backends(hook_api)\n    else:\n        if isinstance(backends_method, str):\n            backend_names = [backends_method]\n        else:\n            assert isinstance(backends_method, list), \"User-provided backend name(s) must be either a string or a list!\"\n            backend_names = backends_method\n\n    logger.info(\"Selected matplotlib backends: %r\", backend_names)\n\n    # Set module names as hiddenimports\n    module_names = [_backend_module_name(backend) for backend in backend_names]  # backend name -> module name\n    hook_api.add_imports(*module_names)\n", "path": "PyInstaller/hooks/hook-matplotlib.backends.py"}], "after_files": [{"content": "#-----------------------------------------------------------------------------\n# Copyright (c) 2013-2022, PyInstaller Development Team.\n#\n# Distributed under the terms of the GNU General Public License (version 2\n# or later) with exception for distributing the bootloader.\n#\n# The full license is in the file COPYING.txt, distributed with this software.\n#\n# SPDX-License-Identifier: (GPL-2.0-or-later WITH Bootloader-exception)\n#-----------------------------------------------------------------------------\n\nfrom PyInstaller.compat import is_darwin\nfrom PyInstaller.utils.hooks import logger, get_hook_config\nfrom PyInstaller import isolated\n\n\n@isolated.decorate\ndef _get_configured_default_backend():\n    \"\"\"\n    Return the configured default matplotlib backend name, if available as matplotlib.rcParams['backend'] (or overridden\n    by MPLBACKEND environment variable. If the value of matplotlib.rcParams['backend'] corresponds to the auto-sentinel\n    object, returns None\n    \"\"\"\n    import matplotlib\n    # matplotlib.rcParams overrides the __getitem__ implementation and attempts to determine and load the default\n    # backend using pyplot.switch_backend(). Therefore, use dict.__getitem__().\n    val = dict.__getitem__(matplotlib.rcParams, 'backend')\n    if isinstance(val, str):\n        return val\n    return None\n\n\n@isolated.decorate\ndef _list_available_mpl_backends():\n    \"\"\"\n    Returns the names of all available matplotlib backends.\n    \"\"\"\n    import matplotlib\n    return matplotlib.rcsetup.all_backends\n\n\n@isolated.decorate\ndef _check_mpl_backend_importable(module_name):\n    \"\"\"\n    Attempts to import the given module name (matplotlib backend module).\n\n    Exceptions are propagated to caller.\n    \"\"\"\n    __import__(module_name)\n\n\n# Bytecode scanning\ndef _recursive_scan_code_objects_for_mpl_use(co):\n    \"\"\"\n    Recursively scan the bytecode for occurrences of matplotlib.use() or mpl.use() calls with const arguments, and\n    collect those arguments into list of used matplotlib backend names.\n    \"\"\"\n\n    from PyInstaller.depend.bytecode import any_alias, recursive_function_calls\n\n    mpl_use_names = {\n        *any_alias(\"matplotlib.use\"),\n        *any_alias(\"mpl.use\"),  # matplotlib is commonly aliased as mpl\n    }\n\n    backends = []\n    for calls in recursive_function_calls(co).values():\n        for name, args in calls:\n            # matplotlib.use(backend) or matplotlib.use(backend, force)\n            # We support only literal arguments. Similarly, kwargs are\n            # not supported.\n            if not len(args) in {1, 2} or not isinstance(args[0], str):\n                continue\n            if name in mpl_use_names:\n                backends.append(args[0])\n\n    return backends\n\n\ndef _backend_module_name(name):\n    \"\"\"\n    Converts matplotlib backend name to its corresponding module name.\n\n    Equivalent to matplotlib.cbook._backend_module_name().\n    \"\"\"\n    if name.startswith(\"module://\"):\n        return name[9:]\n    return f\"matplotlib.backends.backend_{name.lower()}\"\n\n\ndef _autodetect_used_backends(hook_api):\n    \"\"\"\n    Returns a list of automatically-discovered matplotlib backends in use, or the name of the default matplotlib\n    backend. Implements the 'auto' backend selection method.\n    \"\"\"\n    # Scan the code for matplotlib.use()\n    modulegraph = hook_api.analysis.graph\n    mpl_code_objs = modulegraph.get_code_using(\"matplotlib\")\n    used_backends = []\n    for name, co in mpl_code_objs.items():\n        used_backends += _recursive_scan_code_objects_for_mpl_use(co)\n\n    if used_backends:\n        return used_backends\n\n    # Determine the default matplotlib backend.\n    #\n    # Ideally, this would be done by calling ``matplotlib.get_backend()``. However, that function tries to switch to the\n    # default backend (calling ``matplotlib.pyplot.switch_backend()``), which seems to occasionally fail on our linux CI\n    # with an error and, on other occasions, returns the headless Agg backend instead of the GUI one (even with display\n    # server running). Furthermore, using ``matplotlib.get_backend()`` returns headless 'Agg' when display server is\n    # unavailable, which is not ideal for automated builds.\n    #\n    # Therefore, we try to emulate ``matplotlib.get_backend()`` ourselves. First, we try to obtain the configured\n    # default backend from settings (rcparams and/or MPLBACKEND environment variable). If that is unavailable, we try to\n    # find the first importable GUI-based backend, using the same list as matplotlib.pyplot.switch_backend() uses for\n    # automatic backend selection. The difference is that we only test whether the backend module is importable, without\n    # trying to switch to it.\n    default_backend = _get_configured_default_backend()  # isolated sub-process\n    if default_backend:\n        logger.info(\"Found configured default matplotlib backend: %s\", default_backend)\n        return [default_backend]\n\n    candidates = [\"Qt5Agg\", \"Gtk3Agg\", \"TkAgg\", \"WxAgg\"]\n    if is_darwin:\n        candidates = [\"MacOSX\"] + candidates\n    logger.info(\"Trying determine the default backend as first importable candidate from the list: %r\", candidates)\n\n    for candidate in candidates:\n        try:\n            module_name = _backend_module_name(candidate)\n            _check_mpl_backend_importable(module_name)  # NOTE: uses an isolated sub-process.\n        except Exception:\n            continue\n        return [candidate]\n\n    # Fall back to headless Agg backend\n    logger.info(\"None of the backend candidates could be imported; falling back to headless Agg!\")\n    return ['Agg']\n\n\ndef _collect_all_importable_backends(hook_api):\n    \"\"\"\n    Returns a list of all importable matplotlib backends. Implements the 'all' backend selection method.\n    \"\"\"\n    # List of the human-readable names of all available backends.\n    backend_names = _list_available_mpl_backends()  # NOTE: retrieved in an isolated sub-process.\n    logger.info(\"All available matplotlib backends: %r\", backend_names)\n\n    # Try to import the module(s).\n    importable_backends = []\n\n    # List of backends to exclude; Qt4 is not supported by PyInstaller anymore.\n    exclude_backends = {'Qt4Agg', 'Qt4Cairo'}\n\n    # Ignore \"CocoaAgg\" on OSes other than Mac OS; attempting to import it on other OSes halts the current\n    # (sub)process without printing output or raising exceptions, preventing reliable detection. Apply the\n    # same logic for the (newer) \"MacOSX\" backend.\n    if not is_darwin:\n        exclude_backends |= {'CocoaAgg', 'MacOSX'}\n\n    # For safety, attempt to import each backend in an isolated sub-process.\n    for backend_name in backend_names:\n        if backend_name in exclude_backends:\n            logger.info('  Matplotlib backend %r: excluded', backend_name)\n            continue\n\n        try:\n            module_name = _backend_module_name(backend_name)\n            _check_mpl_backend_importable(module_name)  # NOTE: uses an isolated sub-process.\n        except Exception:\n            # Backend is not importable, for whatever reason.\n            logger.info('  Matplotlib backend %r: ignored due to import error', backend_name)\n            continue\n\n        logger.info('  Matplotlib backend %r: added', backend_name)\n        importable_backends.append(backend_name)\n\n    return importable_backends\n\n\ndef hook(hook_api):\n    # Backend collection setting\n    backends_method = get_hook_config(hook_api, 'matplotlib', 'backends')\n    if backends_method is None:\n        backends_method = 'auto'  # default method\n\n    # Select backend(s)\n    if backends_method == 'auto':\n        logger.info(\"Matplotlib backend selection method: automatic discovery of used backends\")\n        backend_names = _autodetect_used_backends(hook_api)\n    elif backends_method == 'all':\n        logger.info(\"Matplotlib backend selection method: collection of all importable backends\")\n        backend_names = _collect_all_importable_backends(hook_api)\n    else:\n        logger.info(\"Matplotlib backend selection method: user-provided name(s)\")\n        if isinstance(backends_method, str):\n            backend_names = [backends_method]\n        else:\n            assert isinstance(backends_method, list), \"User-provided backend name(s) must be either a string or a list!\"\n            backend_names = backends_method\n\n    logger.info(\"Selected matplotlib backends: %r\", backend_names)\n\n    # Set module names as hiddenimports\n    module_names = [_backend_module_name(backend) for backend in backend_names]  # backend name -> module name\n    hook_api.add_imports(*module_names)\n", "path": "PyInstaller/hooks/hook-matplotlib.backends.py"}]}