Skip to content

Commit

Permalink
Make imageio an optional dependency (pyvista#4333)
Browse files Browse the repository at this point in the history
* Make imageio an optional dependency

* Run pre-commit

* make imageio optional in tests

* Apply suggestions from code review

* Update requirements_docs.txt

* Update pyproject.toml

* improve coverage

* hide try_imageio_imread

---------

Co-authored-by: Alex Kaszynski <[email protected]>
Co-authored-by: Tetsuo Koyama <[email protected]>
  • Loading branch information
3 people authored Apr 26, 2023
1 parent 95efdd6 commit 302693b
Show file tree
Hide file tree
Showing 12 changed files with 110 additions and 18 deletions.
14 changes: 12 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ classifiers = [
'Programming Language :: Python :: 3.11',
]
dependencies = [
'imageio',
'matplotlib>=3.0.1',
'numpy',
'pillow',
Expand All @@ -38,18 +37,29 @@ dynamic = ['version']

[project.optional-dependencies]
all = [
'imageio',
'cmocean',
'colorcet',
'ipyvtklink',
'ipywidgets',
'jupyter-server-proxy',
'meshio>=5.2',
'nest_asyncio',
'panel',
'pythreejs',
'trame-client>=2.4.2',
'trame-server>=2.8.0',
'trame-vtk>=2.4.0',
'trame>=2.2.6',
]
colormaps = [
'cmocean',
'colorcet',
]
io = ['meshio>=5.2']
io = [
'imageio',
'meshio>=5.2'
]
jupyter = [
'ipyvtklink',
'ipywidgets',
Expand Down
8 changes: 3 additions & 5 deletions pyvista/core/texture.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
import pyvista as pv
from pyvista import _vtk
from pyvista.plotting.opts import AnnotatedIntEnum
from pyvista.utilities.misc import PyVistaDeprecationWarning
from pyvista.utilities.misc import PyVistaDeprecationWarning, _try_imageio_imread

from .dataset import DataObject

Expand Down Expand Up @@ -148,10 +148,8 @@ def _from_file(self, filename, **kwargs):
if image.n_points < 2:
raise RuntimeError("Problem reading the image with VTK.") # pragma: no cover
self._from_image_data(image)
except (KeyError, ValueError):
from imageio import imread

self._from_array(imread(filename))
except (KeyError, ValueError, OSError):
self._from_array(_try_imageio_imread(filename)) # pragma: no cover

def _from_texture(self, texture):
image = texture.GetInput()
Expand Down
18 changes: 16 additions & 2 deletions pyvista/plotting/plotting.py
Original file line number Diff line number Diff line change
Expand Up @@ -4811,6 +4811,8 @@ def add_text(
def open_movie(self, filename, framerate=24, quality=5, **kwargs):
"""Establish a connection to the ffmpeg writer.
Requires ``imageio`` to be installed.
Parameters
----------
filename : str
Expand Down Expand Up @@ -4842,7 +4844,12 @@ def open_movie(self, filename, framerate=24, quality=5, **kwargs):
>>> pl.open_movie('movie.mp4', quality=10) # doctest:+SKIP
"""
from imageio import get_writer
try:
from imageio import get_writer
except ModuleNotFoundError: # pragma: no cover
raise ModuleNotFoundError(
'Install imageio to use `open_movie` with:\n\n pip install imageio'
) from None

if isinstance(pyvista.FIGURE_PATH, str) and not os.path.isabs(filename):
filename = os.path.join(pyvista.FIGURE_PATH, filename)
Expand All @@ -4851,6 +4858,8 @@ def open_movie(self, filename, framerate=24, quality=5, **kwargs):
def open_gif(self, filename, loop=0, fps=10, palettesize=256, subrectangles=False, **kwargs):
"""Open a gif file.
Requires ``imageio`` to be installed.
Parameters
----------
filename : str
Expand Down Expand Up @@ -4899,7 +4908,12 @@ def open_gif(self, filename, loop=0, fps=10, palettesize=256, subrectangles=Fals
See :ref:`gif_movie_example` for a full example using this method.
"""
from imageio import get_writer
try:
from imageio import get_writer
except ModuleNotFoundError: # pragma: no cover
raise ModuleNotFoundError(
'Install imageio to use `open_gif` with:\n\n pip install imageio'
) from None

if filename[-3:] != 'gif':
raise ValueError('Unsupported filetype. Must end in .gif')
Expand Down
3 changes: 2 additions & 1 deletion pyvista/utilities/errors.py
Original file line number Diff line number Diff line change
Expand Up @@ -337,10 +337,11 @@ class Report(scooby.Report):
def __init__(self, additional=None, ncol=3, text_width=80, sort=False, gpu=True):
"""Generate a :class:`scooby.Report` instance."""
# Mandatory packages
core = ['pyvista', 'vtk', 'numpy', 'matplotlib', 'imageio', 'scooby', 'pooch']
core = ['pyvista', 'vtk', 'numpy', 'matplotlib', 'scooby', 'pooch']

# Optional packages.
optional = [
'imageio',
'pyvistaqt',
'PyQt5',
'IPython',
Expand Down
8 changes: 5 additions & 3 deletions pyvista/utilities/fileio.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

import pyvista
from pyvista import _vtk
from pyvista.utilities.misc import PyVistaDeprecationWarning
from pyvista.utilities.misc import PyVistaDeprecationWarning, _try_imageio_imread


def _get_ext_force(filename, force_ext=None):
Expand Down Expand Up @@ -232,6 +232,9 @@ def _apply_attrs_to_reader(reader, attrs):
def read_texture(filename, attrs=None, progress_bar=False):
"""Load a texture from an image file.
Will attempt to read any file type supported by ``vtk``, however
if it fails, it will attempt to use ``imageio`` to read the file.
Parameters
----------
filename : str
Expand Down Expand Up @@ -276,9 +279,8 @@ def read_texture(filename, attrs=None, progress_bar=False):
except (KeyError, ValueError):
# Otherwise, use the imageio reader
pass
import imageio

return pyvista.Texture(imageio.imread(filename))
return pyvista.Texture(_try_imageio_imread(filename)) # pragma: no cover


def read_exodus(
Expand Down
32 changes: 32 additions & 0 deletions pyvista/utilities/misc.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,38 @@ def _check_range(value, rng, parm_name):
)


def _try_imageio_imread(filename):
"""Attempt to read a file using ``imageio.imread``.
Parameters
----------
filename : str
Name of the file to read using ``imageio``.
Returns
-------
imageio.core.util.Array
Image read from ``imageio``.
Raises
------
ModuleNotFoundError
Raised when ``imageio`` is not installed when attempting to read
``filename``.
"""
try:
from imageio import imread
except ModuleNotFoundError: # pragma: no cover
raise ModuleNotFoundError(
'Problem reading the image with VTK. Install imageio to try to read the '
'file using imageio with:\n\n'
' pip install imageio'
) from None

return imread(filename)


@lru_cache(maxsize=None)
def has_module(module_name):
"""Return if a module can be imported."""
Expand Down
1 change: 0 additions & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
# This is to prevent installing `vtk` from PyPI
# when using a custom variant like `vtk_osmesa`
# which we do in some of our CI jobs.
imageio<2.28.0
matplotlib<3.7.2
numpy<1.25.0
pillow<9.6.0
Expand Down
1 change: 1 addition & 0 deletions requirements_docs.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
cmocean==3.0.3
colorcet==3.0.1
enum-tools==0.9.0.post1
imageio==2.27.0
imageio-ffmpeg==0.4.8
ipygany==0.5.0
ipyvtklink==0.2.3
Expand Down
1 change: 1 addition & 0 deletions requirements_test.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
cmocean<3.1
colorcet<3.1.0
hypothesis<6.74.1
imageio<2.28.0
imageio-ffmpeg<0.5.0
ipygany<0.6.0
ipython<9.0.0
Expand Down
19 changes: 16 additions & 3 deletions tests/plotting/test_plotting.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
import time

from PIL import Image
import imageio
import matplotlib
import numpy as np
import pytest
Expand All @@ -33,18 +32,28 @@
if not system_supports_plotting():
pytestmark = pytest.mark.skip(reason='Requires system to support plotting')

HAS_IMAGEIO = True
try:
import imageio
except ModuleNotFoundError:
HAS_IMAGEIO = False


ffmpeg_failed = False
try:
try:
import imageio_ffmpeg

imageio_ffmpeg.get_ffmpeg_exe()
except ImportError:
imageio.plugins.ffmpeg.download()
except ImportError as err:
if HAS_IMAGEIO:
imageio.plugins.ffmpeg.download()
else:
raise err
except: # noqa: E722
ffmpeg_failed = True


THIS_PATH = pathlib.Path(__file__).parent.absolute()


Expand Down Expand Up @@ -763,13 +772,15 @@ def test_add_lines_invalid():
plotter.add_lines(range(10))


@pytest.mark.skipif(not HAS_IMAGEIO, reason="Requires imageio")
def test_open_gif_invalid():
plotter = pyvista.Plotter()
with pytest.raises(ValueError):
plotter.open_gif('file.abs')


@pytest.mark.skipif(ffmpeg_failed, reason="Requires imageio-ffmpeg")
@pytest.mark.skipif(not HAS_IMAGEIO, reason="Requires imageio")
def test_make_movie(sphere, tmpdir, verify_image_cache):
verify_image_cache.skip = True

Expand Down Expand Up @@ -1228,6 +1239,7 @@ def test_plot_texture_associated():
plotter.show()


@pytest.mark.skipif(not HAS_IMAGEIO, reason="Requires imageio")
def test_read_texture_from_numpy():
"""Test adding a texture to a plot"""
globe = examples.load_globe()
Expand Down Expand Up @@ -2509,6 +2521,7 @@ def test_pointset_plot_as_points_vtk():
pl.show()


@pytest.mark.skipif(not HAS_IMAGEIO, reason="Requires imageio")
def test_write_gif(sphere, tmpdir):
basename = 'write_gif.gif'
path = str(tmpdir.join(basename))
Expand Down
15 changes: 14 additions & 1 deletion tests/utilities/test_misc.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,14 @@

import pytest

from pyvista.utilities.misc import _set_plot_theme_from_env, has_module
from pyvista import examples
from pyvista.utilities.misc import _set_plot_theme_from_env, _try_imageio_imread, has_module

HAS_IMAGEIO = True
try:
import imageio
except ModuleNotFoundError:
HAS_IMAGEIO = False


def test_set_plot_theme_from_env():
Expand All @@ -17,3 +24,9 @@ def test_set_plot_theme_from_env():
def test_has_module():
assert has_module('pytest')
assert not has_module('not_a_module')


@pytest.mark.skipif(not HAS_IMAGEIO, reason="Requires imageio")
def test_try_imageio_imread():
img = _try_imageio_imread(examples.mapfile)
assert isinstance(img, imageio.core.util.Array)
8 changes: 8 additions & 0 deletions tests/utilities/test_reader.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,13 @@
from pyvista import examples
from pyvista.examples.downloads import download_file

HAS_IMAGEIO = True
try:
import imageio # noqa: F401
except ModuleNotFoundError:
HAS_IMAGEIO = False


pytestmark = pytest.mark.skipif(
platform.system() == 'Darwin', reason='MacOS testing on Azure fails when downloading'
)
Expand Down Expand Up @@ -924,6 +931,7 @@ def test_hdf_reader():
assert mesh.n_cells == 4800


@pytest.mark.skipif(not HAS_IMAGEIO, reason="Requires imageio")
def test_gif_reader(gif_file):
reader = pyvista.get_reader(gif_file)
assert isinstance(reader, pyvista.GIFReader)
Expand Down

0 comments on commit 302693b

Please sign in to comment.