1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881
|
# -*- coding: utf-8 -*-
# imageio is distributed under the terms of the (new) BSD License.
"""
.. note::
imageio is under construction, some details with regard to the
Reader and Writer classes may change.
These are the main classes of imageio. They expose an interface for
advanced users and plugin developers. A brief overview:
* imageio.FormatManager - for keeping track of registered formats.
* imageio.Format - representation of a file format reader/writer
* imageio.Format.Reader - object used during the reading of a file.
* imageio.Format.Writer - object used during saving a file.
* imageio.Request - used to store the filename and other info.
Plugins need to implement a Format class and register
a format object using ``imageio.formats.add_format()``.
"""
# todo: do we even use the known extensions?
# Some notes:
#
# The classes in this module use the Request object to pass filename and
# related info around. This request object is instantiated in
# imageio.get_reader and imageio.get_writer.
import sys
import warnings
import contextlib
import numpy as np
from pathlib import Path
from . import Array, asarray
from .request import ImageMode
from ..config import known_plugins, known_extensions, PluginConfig, FileExtension
from ..config.plugins import _original_order
from .imopen import imopen
# survived for backwards compatibility
# I don't know if external plugin code depends on it existing
# We no longer do
MODENAMES = ImageMode
def _get_config(plugin):
"""Old Plugin resolution logic.
Remove once we remove the old format manager.
"""
extension_name = None
if Path(plugin).suffix.lower() in known_extensions:
extension_name = Path(plugin).suffix.lower()
elif plugin in known_plugins:
pass
elif plugin.lower() in known_extensions:
extension_name = plugin.lower()
elif "." + plugin.lower() in known_extensions:
extension_name = "." + plugin.lower()
else:
raise IndexError(f"No format known by name `{plugin}`.")
if extension_name is not None:
for plugin_name in [
x
for file_extension in known_extensions[extension_name]
for x in file_extension.priority
]:
if known_plugins[plugin_name].is_legacy:
plugin = plugin_name
break
return known_plugins[plugin]
class Format(object):
"""Represents an implementation to read/write a particular file format
A format instance is responsible for 1) providing information about
a format; 2) determining whether a certain file can be read/written
with this format; 3) providing a reader/writer class.
Generally, imageio will select the right format and use that to
read/write an image. A format can also be explicitly chosen in all
read/write functions. Use ``print(format)``, or ``help(format_name)``
to see its documentation.
To implement a specific format, one should create a subclass of
Format and the Format.Reader and Format.Writer classes. See
:class:`imageio.plugins` for details.
Parameters
----------
name : str
A short name of this format. Users can select a format using its name.
description : str
A one-line description of the format.
extensions : str | list | None
List of filename extensions that this format supports. If a
string is passed it should be space or comma separated. The
extensions are used in the documentation and to allow users to
select a format by file extension. It is not used to determine
what format to use for reading/saving a file.
modes : str
A string containing the modes that this format can handle ('iIvV'),
“i” for an image, “I” for multiple images, “v” for a volume,
“V” for multiple volumes.
This attribute is used in the documentation and to select the
formats when reading/saving a file.
"""
def __init__(self, name, description, extensions=None, modes=None):
"""Initialize the Plugin.
Parameters
----------
name : str
A short name of this format. Users can select a format using its name.
description : str
A one-line description of the format.
extensions : str | list | None
List of filename extensions that this format supports. If a
string is passed it should be space or comma separated. The
extensions are used in the documentation and to allow users to
select a format by file extension. It is not used to determine
what format to use for reading/saving a file.
modes : str
A string containing the modes that this format can handle ('iIvV'),
“i” for an image, “I” for multiple images, “v” for a volume,
“V” for multiple volumes.
This attribute is used in the documentation and to select the
formats when reading/saving a file.
"""
# Store name and description
self._name = name.upper()
self._description = description
# Store extensions, do some effort to normalize them.
# They are stored as a list of lowercase strings without leading dots.
if extensions is None:
extensions = []
elif isinstance(extensions, str):
extensions = extensions.replace(",", " ").split(" ")
#
if isinstance(extensions, (tuple, list)):
self._extensions = tuple(
["." + e.strip(".").lower() for e in extensions if e]
)
else:
raise ValueError("Invalid value for extensions given.")
# Store mode
self._modes = modes or ""
if not isinstance(self._modes, str):
raise ValueError("Invalid value for modes given.")
for m in self._modes:
if m not in "iIvV?":
raise ValueError("Invalid value for mode given.")
def __repr__(self):
# Short description
return "<Format %s - %s>" % (self.name, self.description)
def __str__(self):
return self.doc
@property
def doc(self):
"""The documentation for this format (name + description + docstring)."""
# Our docsring is assumed to be indented by four spaces. The
# first line needs special attention.
return "%s - %s\n\n %s\n" % (
self.name,
self.description,
self.__doc__.strip(),
)
@property
def name(self):
"""The name of this format."""
return self._name
@property
def description(self):
"""A short description of this format."""
return self._description
@property
def extensions(self):
"""A list of file extensions supported by this plugin.
These are all lowercase with a leading dot.
"""
return self._extensions
@property
def modes(self):
"""A string specifying the modes that this format can handle."""
return self._modes
def get_reader(self, request):
"""get_reader(request)
Return a reader object that can be used to read data and info
from the given file. Users are encouraged to use
imageio.get_reader() instead.
"""
select_mode = request.mode[1] if request.mode[1] in "iIvV" else ""
if select_mode not in self.modes:
raise RuntimeError(
f"Format {self.name} cannot read in {request.mode.image_mode} mode"
)
return self.Reader(self, request)
def get_writer(self, request):
"""get_writer(request)
Return a writer object that can be used to write data and info
to the given file. Users are encouraged to use
imageio.get_writer() instead.
"""
select_mode = request.mode[1] if request.mode[1] in "iIvV" else ""
if select_mode not in self.modes:
raise RuntimeError(
f"Format {self.name} cannot write in {request.mode.image_mode} mode"
)
return self.Writer(self, request)
def can_read(self, request):
"""can_read(request)
Get whether this format can read data from the specified uri.
"""
return self._can_read(request)
def can_write(self, request):
"""can_write(request)
Get whether this format can write data to the speciefed uri.
"""
return self._can_write(request)
def _can_read(self, request): # pragma: no cover
"""Check if Plugin can read from ImageResource.
This method is called when the format manager is searching for a format
to read a certain image. Return True if this format can do it.
The format manager is aware of the extensions and the modes that each
format can handle. It will first ask all formats that *seem* to be able
to read it whether they can. If none can, it will ask the remaining
formats if they can: the extension might be missing, and this allows
formats to provide functionality for certain extensions, while giving
preference to other plugins.
If a format says it can, it should live up to it. The format would
ideally check the request.firstbytes and look for a header of some kind.
Parameters
----------
request : Request
A request that can be used to access the ImageResource and obtain
metadata about it.
Returns
-------
can_read : bool
True if the plugin can read from the ImageResource, False otherwise.
"""
return None # Plugins must implement this
def _can_write(self, request): # pragma: no cover
"""Check if Plugin can write to ImageResource.
Parameters
----------
request : Request
A request that can be used to access the ImageResource and obtain
metadata about it.
Returns
-------
can_read : bool
True if the plugin can write to the ImageResource, False otherwise.
"""
return None # Plugins must implement this
# -----
class _BaseReaderWriter(object):
"""Base class for the Reader and Writer class to implement common
functionality. It implements a similar approach for opening/closing
and context management as Python's file objects.
"""
def __init__(self, format, request):
self.__closed = False
self._BaseReaderWriter_last_index = -1
self._format = format
self._request = request
# Open the reader/writer
self._open(**self.request.kwargs.copy())
@property
def format(self):
"""The :class:`.Format` object corresponding to the current
read/write operation.
"""
return self._format
@property
def request(self):
"""The :class:`.Request` object corresponding to the
current read/write operation.
"""
return self._request
def __enter__(self):
self._checkClosed()
return self
def __exit__(self, type, value, traceback):
if value is None:
# Otherwise error in close hide the real error.
self.close()
def __del__(self):
try:
self.close()
except Exception: # pragma: no cover
pass # Suppress noise when called during interpreter shutdown
def close(self):
"""Flush and close the reader/writer.
This method has no effect if it is already closed.
"""
if self.__closed:
return
self.__closed = True
self._close()
# Process results and clean request object
self.request.finish()
@property
def closed(self):
"""Whether the reader/writer is closed."""
return self.__closed
def _checkClosed(self, msg=None):
"""Internal: raise an ValueError if reader/writer is closed"""
if self.closed:
what = self.__class__.__name__
msg = msg or ("I/O operation on closed %s." % what)
raise RuntimeError(msg)
# To implement
def _open(self, **kwargs):
"""_open(**kwargs)
Plugins should probably implement this.
It is called when reader/writer is created. Here the
plugin can do its initialization. The given keyword arguments
are those that were given by the user at imageio.read() or
imageio.write().
"""
raise NotImplementedError()
def _close(self):
"""_close()
Plugins should probably implement this.
It is called when the reader/writer is closed. Here the plugin
can do a cleanup, flush, etc.
"""
raise NotImplementedError()
# -----
class Reader(_BaseReaderWriter):
"""
The purpose of a reader object is to read data from an image
resource, and should be obtained by calling :func:`.get_reader`.
A reader can be used as an iterator to read multiple images,
and (if the format permits) only reads data from the file when
new data is requested (i.e. streaming). A reader can also be
used as a context manager so that it is automatically closed.
Plugins implement Reader's for different formats. Though rare,
plugins may provide additional functionality (beyond what is
provided by the base reader class).
"""
def get_length(self):
"""get_length()
Get the number of images in the file. (Note: you can also
use ``len(reader_object)``.)
The result can be:
* 0 for files that only have meta data
* 1 for singleton images (e.g. in PNG, JPEG, etc.)
* N for image series
* inf for streams (series of unknown length)
"""
return self._get_length()
def get_data(self, index, **kwargs):
"""get_data(index, **kwargs)
Read image data from the file, using the image index. The
returned image has a 'meta' attribute with the meta data.
Raises IndexError if the index is out of range.
Some formats may support additional keyword arguments. These are
listed in the documentation of those formats.
"""
self._checkClosed()
self._BaseReaderWriter_last_index = index
try:
im, meta = self._get_data(index, **kwargs)
except StopIteration:
raise IndexError(index)
return Array(im, meta) # Array tests im and meta
def get_next_data(self, **kwargs):
"""get_next_data(**kwargs)
Read the next image from the series.
Some formats may support additional keyword arguments. These are
listed in the documentation of those formats.
"""
return self.get_data(self._BaseReaderWriter_last_index + 1, **kwargs)
def set_image_index(self, index, **kwargs):
"""set_image_index(index)
Set the internal pointer such that the next call to
get_next_data() returns the image specified by the index
"""
self._checkClosed()
n = self.get_length()
self._BaseReaderWriter_last_index = min(max(index - 1, -1), n)
def get_meta_data(self, index=None):
"""get_meta_data(index=None)
Read meta data from the file. using the image index. If the
index is omitted or None, return the file's (global) meta data.
Note that ``get_data`` also provides the meta data for the returned
image as an attribute of that image.
The meta data is a dict, which shape depends on the format.
E.g. for JPEG, the dict maps group names to subdicts and each
group is a dict with name-value pairs. The groups represent
the different metadata formats (EXIF, XMP, etc.).
"""
self._checkClosed()
meta = self._get_meta_data(index)
if not isinstance(meta, dict):
raise ValueError(
"Meta data must be a dict, not %r" % meta.__class__.__name__
)
return meta
def iter_data(self):
"""iter_data()
Iterate over all images in the series. (Note: you can also
iterate over the reader object.)
"""
self._checkClosed()
n = self.get_length()
i = 0
while i < n:
try:
im, meta = self._get_data(i)
except StopIteration:
return
except IndexError:
if n == float("inf"):
return
raise
yield Array(im, meta)
i += 1
# Compatibility
def __iter__(self):
return self.iter_data()
def __len__(self):
n = self.get_length()
if n == float("inf"):
n = sys.maxsize
return n
# To implement
def _get_length(self):
"""_get_length()
Plugins must implement this.
The returned scalar specifies the number of images in the series.
See Reader.get_length for more information.
"""
raise NotImplementedError()
def _get_data(self, index):
"""_get_data()
Plugins must implement this, but may raise an IndexError in
case the plugin does not support random access.
It should return the image and meta data: (ndarray, dict).
"""
raise NotImplementedError()
def _get_meta_data(self, index):
"""_get_meta_data(index)
Plugins must implement this.
It should return the meta data as a dict, corresponding to the
given index, or to the file's (global) meta data if index is
None.
"""
raise NotImplementedError()
# -----
class Writer(_BaseReaderWriter):
"""
The purpose of a writer object is to write data to an image
resource, and should be obtained by calling :func:`.get_writer`.
A writer will (if the format permits) write data to the file
as soon as new data is provided (i.e. streaming). A writer can
also be used as a context manager so that it is automatically
closed.
Plugins implement Writer's for different formats. Though rare,
plugins may provide additional functionality (beyond what is
provided by the base writer class).
"""
def append_data(self, im, meta=None):
"""append_data(im, meta={})
Append an image (and meta data) to the file. The final meta
data that is used consists of the meta data on the given
image (if applicable), updated with the given meta data.
"""
self._checkClosed()
# Check image data
if not isinstance(im, np.ndarray):
raise ValueError("append_data requires ndarray as first arg")
# Get total meta dict
total_meta = {}
if hasattr(im, "meta") and isinstance(im.meta, dict):
total_meta.update(im.meta)
if meta is None:
pass
elif not isinstance(meta, dict):
raise ValueError("Meta must be a dict.")
else:
total_meta.update(meta)
# Decouple meta info
im = asarray(im)
# Call
return self._append_data(im, total_meta)
def set_meta_data(self, meta):
"""set_meta_data(meta)
Sets the file's (global) meta data. The meta data is a dict which
shape depends on the format. E.g. for JPEG the dict maps
group names to subdicts, and each group is a dict with
name-value pairs. The groups represents the different
metadata formats (EXIF, XMP, etc.).
Note that some meta formats may not be supported for
writing, and individual fields may be ignored without
warning if they are invalid.
"""
self._checkClosed()
if not isinstance(meta, dict):
raise ValueError("Meta must be a dict.")
else:
return self._set_meta_data(meta)
# To implement
def _append_data(self, im, meta):
# Plugins must implement this
raise NotImplementedError()
def _set_meta_data(self, meta):
# Plugins must implement this
raise NotImplementedError()
class FormatManager(object):
"""
The FormatManager is a singleton plugin factory.
The format manager supports getting a format object using indexing (by
format name or extension). When used as an iterator, this object
yields all registered format objects.
See also :func:`.help`.
"""
@property
def _formats(self):
available_formats = list()
for config in known_plugins.values():
with contextlib.suppress(ImportError):
# if an exception is raised, then format not installed
if config.is_legacy and config.format is not None:
available_formats.append(config)
return available_formats
def __repr__(self):
return f"<imageio.FormatManager with {len(self._formats)} registered formats>"
def __iter__(self):
return iter(x.format for x in self._formats)
def __len__(self):
return len(self._formats)
def __str__(self):
ss = []
for config in self._formats:
ext = config.legacy_args["extensions"]
desc = config.legacy_args["description"]
s = f"{config.name} - {desc} [{ext}]"
ss.append(s)
return "\n".join(ss)
def __getitem__(self, name):
warnings.warn(
"The usage of `FormatManager` is deprecated and it will be "
"removed in Imageio v3. Use `iio.imopen` instead.",
DeprecationWarning,
stacklevel=2,
)
if not isinstance(name, str):
raise ValueError(
"Looking up a format should be done by name or by extension."
)
if name == "":
raise ValueError("No format matches the empty string.")
# Test if name is existing file
if Path(name).is_file():
# legacy compatibility - why test reading here??
try:
return imopen(name, "r", legacy_mode=True)._format
except ValueError:
# no plugin can read the file
pass
config = _get_config(name.upper())
try:
return config.format
except ImportError:
raise ImportError(
f"The `{config.name}` format is not installed. "
f"Use `pip install imageio[{config.install_name}]` to install it."
)
def sort(self, *names):
"""sort(name1, name2, name3, ...)
Sort the formats based on zero or more given names; a format with
a name that matches one of the given names will take precedence
over other formats. A match means an equal name, or ending with
that name (though the former counts higher). Case insensitive.
Format preference will match the order of the given names: using
``sort('TIFF', '-FI', '-PIL')`` would prefer the FreeImage formats
over the Pillow formats, but prefer TIFF even more. Each time
this is called, the starting point is the default format order,
and calling ``sort()`` with no arguments will reset the order.
Be aware that using the function can affect the behavior of
other code that makes use of imageio.
Also see the ``IMAGEIO_FORMAT_ORDER`` environment variable.
"""
warnings.warn(
"`FormatManager` is deprecated and it will be removed in ImageIO v3."
" Migrating `FormatManager.sort` depends on your use-case:\n"
"\t- modify `iio.config.known_plugins` to specify the search order for "
"unrecognized formats.\n"
"\t- modify `iio.config.known_extensions[<extension>].priority`"
" to control a specific extension.",
DeprecationWarning,
stacklevel=2,
)
# Check and sanitize input
for name in names:
if not isinstance(name, str):
raise TypeError("formats.sort() accepts only string names.")
if any(c in name for c in ".,"):
raise ValueError(
"Names given to formats.sort() should not "
"contain dots `.` or commas `,`."
)
should_reset = len(names) == 0
if should_reset:
names = _original_order
sane_names = [name.strip().upper() for name in names if name != ""]
# enforce order for every extension that uses it
flat_extensions = [
ext for ext_list in known_extensions.values() for ext in ext_list
]
for extension in flat_extensions:
if should_reset:
extension.reset()
continue
for name in reversed(sane_names):
for plugin in [x for x in extension.default_priority]:
if plugin.endswith(name):
extension.priority.remove(plugin)
extension.priority.insert(0, plugin)
old_order = known_plugins.copy()
known_plugins.clear()
for name in sane_names:
plugin = old_order.pop(name, None)
if plugin is not None:
known_plugins[name] = plugin
known_plugins.update(old_order)
def add_format(self, iio_format, overwrite=False):
"""add_format(format, overwrite=False)
Register a format, so that imageio can use it. If a format with the
same name already exists, an error is raised, unless overwrite is True,
in which case the current format is replaced.
"""
warnings.warn(
"`FormatManager` is deprecated and it will be removed in ImageIO v3."
"To migrate `FormatManager.add_format` add the plugin directly to "
"`iio.config.known_plugins`.",
DeprecationWarning,
stacklevel=2,
)
if not isinstance(iio_format, Format):
raise ValueError("add_format needs argument to be a Format object")
elif not overwrite and iio_format.name in self.get_format_names():
raise ValueError(
f"A Format named {iio_format.name} is already registered, use"
" `overwrite=True` to replace."
)
config = PluginConfig(
name=iio_format.name.upper(),
class_name=iio_format.__class__.__name__,
module_name=iio_format.__class__.__module__,
is_legacy=True,
install_name="unknown",
legacy_args={
"name": iio_format.name,
"description": iio_format.description,
"extensions": " ".join(iio_format.extensions),
"modes": iio_format.modes,
},
)
known_plugins[config.name] = config
for extension in iio_format.extensions:
# be conservative and always treat it as a unique file format
ext = FileExtension(
extension=extension,
priority=[config.name],
name="Unique Format",
description="A format inserted at runtime."
f" It is being read by the `{config.name}` plugin.",
)
known_extensions.setdefault(extension, list()).append(ext)
def search_read_format(self, request):
"""search_read_format(request)
Search a format that can read a file according to the given request.
Returns None if no appropriate format was found. (used internally)
"""
try:
# in legacy_mode imopen returns a LegacyPlugin
return imopen(request, request.mode.io_mode, legacy_mode=True)._format
except AttributeError:
warnings.warn(
"ImageIO now uses a v3 plugin when reading this format."
" Please migrate to the v3 API (preferred) or use imageio.v2.",
DeprecationWarning,
stacklevel=2,
)
return None
except ValueError:
# no plugin can read this request
# but the legacy API doesn't raise
return None
def search_write_format(self, request):
"""search_write_format(request)
Search a format that can write a file according to the given request.
Returns None if no appropriate format was found. (used internally)
"""
try:
# in legacy_mode imopen returns a LegacyPlugin
return imopen(request, request.mode.io_mode, legacy_mode=True)._format
except AttributeError:
warnings.warn(
"ImageIO now uses a v3 plugin when writing this format."
" Please migrate to the v3 API (preferred) or use imageio.v2.",
DeprecationWarning,
stacklevel=2,
)
return None
except ValueError:
# no plugin can write this request
# but the legacy API doesn't raise
return None
def get_format_names(self):
"""Get the names of all registered formats."""
warnings.warn(
"`FormatManager` is deprecated and it will be removed in ImageIO v3."
"To migrate `FormatManager.get_format_names` use `iio.config.known_plugins.keys()` instead.",
DeprecationWarning,
stacklevel=2,
)
return [f.name for f in self._formats]
def show(self):
"""Show a nicely formatted list of available formats"""
print(self)
|