Repository: pyinstaller/pyinstaller

Issue:
Prevent Matplotlib's backend from being collected more than once
**Is your feature request related to a problem? Please describe.**
Hi, I've noticed that some of `Matplotlib`'s backend are collected more than once during building. While this does not cause building to fail, it would be more readable to remove duplicate items from the list shown in the building log.

**Describe the solution you'd like**
Prevent `Matplotlib`'s backend from being collected into the list.

**Describe alternatives you've considered**
Remove duplicate items from the list after collection.

**Additional context**
`PyInstaller` 6.0
```
136360 INFO: Loading module hook 'hook-matplotlib.backends.py' from 'D:\\Python\\lib\\site-packages\\PyInstaller\\hooks'...
136361 INFO: Matplotlib backend selection method: automatic discovery of used backends
136374 INFO: Discovered Matplotlib backend(s) via `matplotlib.use()` call in module 'E:\\Wordless\\wordless\\wl_main.py': ['Qt5Agg']
136380 INFO: Discovered Matplotlib backend(s) via `matplotlib.use()` call in module 'sympy.testing.runtests': ['Agg', 'Agg']
136391 INFO: The following Matplotlib backends were discovered by scanning for `matplotlib.use()` calls: ['Qt5Agg', 'Agg', 'Agg']. If your backend of choice is not in this list, either add a `matplotlib.use()` call to your code, or configure the backend collection via hook options (see: https://pyinstaller.org/en/stable/hooks-config.html#matplotlib-hooks).
136391 INFO: Selected matplotlib backends: ['Qt5Agg', 'Agg', 'Agg']
```



Discussion:
You mean it should say:
```
136374 INFO: Discovered Matplotlib backend(s) via `matplotlib.use()` call in module 'E:\\Wordless\\wordless\\wl_main.py': ['Qt5Agg']
136380 INFO: Discovered Matplotlib backend(s) via `matplotlib.use()` call in module 'sympy.testing.runtests': ['Agg']
136391 INFO: The following Matplotlib backends were discovered by scanning for `matplotlib.use()` calls: ['Qt5Agg', 'Agg']. If your backend of choice is not in this list, either add a `matplotlib.use()` call to your code, or configure the backend collection via hook options (see: https://pyinstaller.org/en/stable/hooks-config.html#matplotlib-hooks).
136391 INFO: Selected matplotlib backends: ['Qt5Agg', 'Agg']
```
i.e. Each list should be deduplicated?

To be clear, you're not going to get two copies of the `Agg` backend in your application so the implications of this will never go beyond the log file.
Yes, that's good if no extra copies are included in the frozen app, but the output list should be deduplicated to make it clear to users.
That sounds reasonable - we should de-duplicate the obtained list of backends; first [here](https://github.com/pyinstaller/pyinstaller/blob/362ff1b9090748839b1d19f50c78abc79c1039b0/PyInstaller/hooks/hook-matplotlib.backends.py#L108), and then finally [here](https://github.com/pyinstaller/pyinstaller/blob/362ff1b9090748839b1d19f50c78abc79c1039b0/PyInstaller/hooks/hook-matplotlib.backends.py#L214).

Resolving patch:
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
@@ -105,6 +105,9 @@ def _autodetect_used_backends(hook_api):
             )
             used_backends += co_backends
 
+    # Deduplicate and sort the list of used backends before displaying it.
+    used_backends = sorted(set(used_backends))
+
     if used_backends:
         HOOK_CONFIG_DOCS = 'https://pyinstaller.org/en/stable/hooks-config.html#matplotlib-hooks'
         logger.info(
@@ -211,6 +214,9 @@ def hook(hook_api):
             assert isinstance(backends_method, list), "User-provided backend name(s) must be either a string or a list!"
             backend_names = backends_method
 
+    # Deduplicate and sort the list of selected backends before displaying it.
+    backend_names = sorted(set(backend_names))
+
     logger.info("Selected matplotlib backends: %r", backend_names)
 
     # Set module names as hiddenimports


Test patch: