Repository: matplotlib/matplotlib
Pull request: Change subprocess for inkscape version detection
Use the `--without-gui` (`-z`) for inkscape to prevent depending on an X server for version detection

## PR Summary

A very small change to how inkscape version detection is implemented in order to prevent depending on access to the interactive X session (on Linux, at least)

Resolves #17354 

(Apologies if this is not the best solution to this issue since I don't really know the internals of `matplotlib` very well)

Problem statement:
Converter detection fails for inkscape if on headless system without DISPLAY
<!--To help us understand and resolve your issue, please fill out the form to the best of your ability.-->
<!--You can feel free to delete the sections that do not apply.-->

### Bug report

**Bug summary**

When running `mpl._get_executable_info('inkscape')` on a system where `DISPLAY` is defined but non-accessible (such as a remote system running a local X server), the method bombs with `subprocess.CalledProcessError: Command '['inkscape', '-V']' returned non-zero exit status 1.`

**Code for reproduction**

<!--A minimum code snippet required to reproduce the bug.
Please make sure to minimize the number of dependencies required, and provide
any necessary plotted data.
Avoid using threads, as Matplotlib is (explicitly) not thread-safe.-->

```python
import matplotlib
matplotlib._get_executable_info('inkscape')
```

**Actual outcome**

<!--The output produced by the above code, which may be a screenshot, console output, etc.-->

```
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/home/jat/.conda/envs/hyperspy_dev/lib/python3.8/site-packages/matplotlib/__init__.py", line 357, in _get_executable_info
    return impl(["inkscape", "-V"], "^Inkscape ([^ ]*)")
  File "/home/jat/.conda/envs/hyperspy_dev/lib/python3.8/site-packages/matplotlib/__init__.py", line 330, in impl
    raise _cpe
  File "/home/jat/.conda/envs/hyperspy_dev/lib/python3.8/site-packages/matplotlib/__init__.py", line 323, in impl
    output = subprocess.check_output(
  File "/home/jat/.conda/envs/hyperspy_dev/lib/python3.8/subprocess.py", line 411, in check_output
    return run(*popenargs, stdout=PIPE, timeout=timeout, check=True,
  File "/home/jat/.conda/envs/hyperspy_dev/lib/python3.8/subprocess.py", line 512, in run
    raise CalledProcessError(retcode, process.args,
subprocess.CalledProcessError: Command '['inkscape', '-V']' returned non-zero exit status 1.
```

**Expected outcome**

<!--A description of the expected outcome from the code snippet-->
<!--If this used to work in an earlier version of Matplotlib, please note the version it used to work on-->

Even if `DISPLAY` is not available, detection of headless inkscape should work

**Matplotlib version**
<!--Please specify your platform and versions of the relevant libraries you are using:-->
  * Operating system: Linux 5.3.0 - Ubuntu 18.04
  * Matplotlib version: 3.1.3 (although this is a problem in the latest version as well)
  * Matplotlib backend (`print(matplotlib.get_backend())`): Qt5Agg
  * Python version: 3.8.2 (Anaconda)

<!--Please tell us how you installed matplotlib and python e.g., from source, pip, conda-->
<!--If you installed from conda, please specify which channel you used if not the default-->

Installed via `conda` using the `conda-forge` channel

**Additional info**

This can be fixed by calling Inkscape in headless mode, rather than interactive mode:

```shell
$ inkscape -V

(inkscape:11828): Gtk-WARNING **: 11:14:28.713: cannot open display: :0
```

vs.

```shell
$ inkscape -z -V
Inkscape 0.92.3 (2405546, 2018-03-11)
```

Hints from issue discussion:
This can also be solved with a simple `unset DISPLAY` from within the ssh session, but I think it would be better to have matplotlib deal with this situation gracefully

Commit message: Fix remaining flake8 issue

Patch to review:
diff --git a/lib/matplotlib/__init__.py b/lib/matplotlib/__init__.py
index 103b650c984b..9e3f3c02dbcd 100644
--- a/lib/matplotlib/__init__.py
+++ b/lib/matplotlib/__init__.py
@@ -330,7 +330,14 @@ def impl(args, regex, min_ver=None, ignore_exit_code=False):
         message = "Failed to find a Ghostscript installation"
         raise ExecutableNotFoundError(message)
     elif name == "inkscape":
-        info = impl(["inkscape", "-V"], "^Inkscape ([^ ]*)")
+        try:
+            # use headless option first (works with inkscape version < 1.0):
+            info = impl(["inkscape", "--without-gui", "-V"],
+                        "Inkscape ([^ ]*)")
+        except ExecutableNotFoundError as _e:
+            # if --without-gui is not accepted, we're using Inkscape v > 1.0
+            # so try without the headless option:
+            info = impl(["inkscape", "-V"], "Inkscape ([^ ]*)")
         if info and info.version >= "1.0":
             raise ExecutableNotFoundError(
                 f"You have Inkscape version {info.version} but Matplotlib "


Code review comments:
diff_hunk: @@ -330,7 +330,14 @@ def impl(args, regex, min_ver=None, ignore_exit_code=False):
         message = "Failed to find a Ghostscript installation"
         raise ExecutableNotFoundError(message)
     elif name == "inkscape":
-        info = impl(["inkscape", "-V"], "^Inkscape ([^ ]*)")
+        try:
+            # use headless option first (works with inkscape version < 1.0):
+            info = impl(["inkscape", "--without-gui", "-V"],
+                        "Inkscape ([^ ]*)")
+        except ExecutableNotFoundError as _e:
path: lib/matplotlib/__init__.py
text: @user1:
`_e` is unused, just drop it.
```suggestion
        except ExecutableNotFoundError:
```