Repository: scverse/scanpy

Issue:
set_figure_params() bug when running at command-line
- [X ] I have checked that this issue has not already been reported.
- [X ] I have confirmed this bug exists on the latest version of scanpy.
- [ ] (optional) I have confirmed this bug exists on the master branch of scanpy.

---

When I run scanpy over an ssh connection to a remote Ubuntu Linux server (so not using the IPython notebooks), the scanpy "set_figure_params(...)" command just outputs an "In :", and I need to exit using [Ctrl][D]: 

### Minimal code sample (that we can copy&paste without having any data)
```
import scanpy as sc
sc.set_figure_params(figsize=(4, 4))
```
The output in console is:
```
In :

In :
```
and need to press [Crtrl][D] to exit, as otherwise this just loops "In :" repeatedly.


If I add "ipython_format=None":
```
sc.set_figure_params(figsize=(4, 4), ipython_format=None)
```
then this continues without any "In :", and I can continue with script.  

I think the reason for this is in the scanpy "_settings.py" file:
```
def set_figure_params(
    ......etc.....
    try:
        import IPython
        if isinstance(ipython_format, str):
                ipython_format = [ipython_format]
        IPython.display.set_matplotlib_formats(*ipython_format)
    except Exception:
        pass
    from matplotlib import rcParams
    .....etc......
```
where the:
```
    IPython.display.set_matplotlib_formats(*ipython_format)
```
produces this "In :" message loop.

I think this bug could be fixed by adding "if self._is_run_from_ipython():" before the "try: import IPython": 
```
def set_figure_params(
    ......etc.....
    if self._is_run_from_ipython():
        try:
            import IPython
            if isinstance(ipython_format, str):
                ipython_format = [ipython_format]
            IPython.display.set_matplotlib_formats(*ipython_format)
        except Exception:
            pass
```
The "try:" 
```
>>> import IPython
```
doesn't throw an exception, as IPython is installed:
```
$ pip show IPython
Name: ipython
Version: 7.18.1
Summary: IPython: Productive Interactive Computing
Home-page: https://ipython.org
Author: The IPython Development Team
Author-email: ipython-dev@python.org
License: BSD
Location: /slipstream/home/sbridgett/miniconda3/lib/python3.8/site-packages
Requires: pygments, jedi, prompt-toolkit, backcall, pexpect, setuptools, decorator, traitlets, pickleshare
Required-by: ipywidgets, ipykernel
```

whereas the _is_run_from_ipython() will correctly ``return False``, as ``__IPYTHON__`` gives an exception:
```
    def _is_run_from_ipython():
        """Determines whether run from Ipython.
        Only affects progress bars.
        """
        try:
            __IPYTHON__
            return True
        except NameError:
            return False
```

#### Versions

<details>

scanpy.logging.print_versions()
-----
anndata     0.7.4
scanpy      1.6.0
sinfo       0.3.1
-----
PIL                 8.0.0
anndata             0.7.4
cairo               1.20.0
cffi                1.14.0
colorama            0.4.4
cycler              0.10.0
cython_runtime      NA
dateutil            2.8.1
get_version         2.1
h5py                2.10.0
igraph              0.8.3
joblib              0.17.0
kiwisolver          1.2.0
legacy_api_wrap     1.2
leidenalg           0.8.2
llvmlite            0.34.0
matplotlib          3.3.2
mkl                 2.3.0
mpl_toolkits        NA
natsort             7.0.1
numba               0.51.2
numexpr             2.7.1
numpy               1.19.2
packaging           20.4
pandas              1.1.3
pkg_resources       NA
pyparsing           2.4.7
pytz                2020.1
scanpy              1.6.0
scipy               1.5.2
setuptools_scm      NA
sinfo               0.3.1
six                 1.14.0
sklearn             0.23.2
tables              3.6.1
texttable           1.6.3
typing_extensions   NA
wcwidth             0.2.5
-----
Python 3.8.3 (default, May 19 2020, 18:47:26) [GCC 7.3.0]
Linux-3.13.0-143-generic-x86_64-with-glibc2.10
32 logical CPU cores, x86_64
-----
Session information updated at 2020-11-01 11:37

</details>



Discussion:

Resolving patch:
diff --git a/scanpy/_settings.py b/scanpy/_settings.py
--- a/scanpy/_settings.py
+++ b/scanpy/_settings.py
@@ -36,7 +36,6 @@ def level(self) -> int:
         # getLevelName(str) returns the int level…
         return getLevelName(_VERBOSITY_TO_LOGLEVEL[self])
 
-
     @contextmanager
     def override(self, verbosity: "Verbosity") -> ContextManager["Verbosity"]:
         """\
@@ -64,6 +63,7 @@ class ScanpyConfig:
     """\
     Config manager for scanpy.
     """
+
     def __init__(
         self,
         *,
@@ -142,8 +142,7 @@ def verbosity(self) -> Verbosity:
     @verbosity.setter
     def verbosity(self, verbosity: Union[Verbosity, int, str]):
         verbosity_str_options = [
-            v for v in _VERBOSITY_TO_LOGLEVEL
-            if isinstance(v, str)
+            v for v in _VERBOSITY_TO_LOGLEVEL if isinstance(v, str)
         ]
         if isinstance(verbosity, Verbosity):
             self._verbosity = verbosity
@@ -164,8 +163,7 @@ def verbosity(self, verbosity: Union[Verbosity, int, str]):
 
     @property
     def plot_suffix(self) -> str:
-        """Global suffix that is appended to figure filenames.
-        """
+        """Global suffix that is appended to figure filenames."""
         return self._plot_suffix
 
     @plot_suffix.setter
@@ -384,11 +382,13 @@ def categories_to_ignore(self, categories_to_ignore: Iterable[str]):
     # --------------------------------------------------------------------------------
 
     # Collected from the print_* functions in matplotlib.backends
+    # fmt: off
     _Format = Literal[
         'png', 'jpg', 'tif', 'tiff',
         'pdf', 'ps', 'eps', 'svg', 'svgz', 'pgf',
         'raw', 'rgba',
     ]
+    # fmt: on
 
     def set_figure_params(
         self,
@@ -439,14 +439,15 @@ def set_figure_params(
             Only concerns the notebook/IPython environment; see
             :func:`~IPython.display.set_matplotlib_formats` for details.
         """
-        try:
+        if self._is_run_from_ipython():
             import IPython
+
             if isinstance(ipython_format, str):
                 ipython_format = [ipython_format]
             IPython.display.set_matplotlib_formats(*ipython_format)
-        except Exception:
-            pass
+
         from matplotlib import rcParams
+
         self._vector_friendly = vector_friendly
         self.file_format_figs = format
         if dpi is not None:
@@ -460,6 +461,7 @@ def set_figure_params(
             rcParams['axes.facecolor'] = facecolor
         if scanpy:
             from .plotting._rcmod import set_rcParams_scanpy
+
             set_rcParams_scanpy(fontsize=fontsize, color_map=color_map)
         if figsize is not None:
             rcParams['figure.figsize'] = figsize
@@ -467,15 +469,9 @@ def set_figure_params(
 
     @staticmethod
     def _is_run_from_ipython():
-        """Determines whether run from Ipython.
-
-        Only affects progress bars.
-        """
-        try:
-            __IPYTHON__
-            return True
-        except NameError:
-            return False
+        """Determines whether we're currently in IPython."""
+        # https://stackoverflow.com/questions/40638507/testing-for-presence-of-ipython
+        return hasattr(__builtins__, "__IPYTHON__")
 
     def __str__(self) -> str:
         return '\n'.join(


Test patch: