Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/imageio-2.35.1-py3.8.egg/imageio/core/format.py: 31%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

293 statements  

1# -*- coding: utf-8 -*- 

2# imageio is distributed under the terms of the (new) BSD License. 

3 

4""" 

5 

6.. note:: 

7 imageio is under construction, some details with regard to the 

8 Reader and Writer classes may change. 

9 

10These are the main classes of imageio. They expose an interface for 

11advanced users and plugin developers. A brief overview: 

12 

13 * imageio.FormatManager - for keeping track of registered formats. 

14 * imageio.Format - representation of a file format reader/writer 

15 * imageio.Format.Reader - object used during the reading of a file. 

16 * imageio.Format.Writer - object used during saving a file. 

17 * imageio.Request - used to store the filename and other info. 

18 

19Plugins need to implement a Format class and register 

20a format object using ``imageio.formats.add_format()``. 

21 

22""" 

23 

24# todo: do we even use the known extensions? 

25 

26# Some notes: 

27# 

28# The classes in this module use the Request object to pass filename and 

29# related info around. This request object is instantiated in 

30# imageio.get_reader and imageio.get_writer. 

31 

32import sys 

33import warnings 

34import contextlib 

35 

36import numpy as np 

37from pathlib import Path 

38 

39from . import Array, asarray 

40from .request import ImageMode 

41from ..config import known_plugins, known_extensions, PluginConfig, FileExtension 

42from ..config.plugins import _original_order 

43from .imopen import imopen 

44 

45 

46# survived for backwards compatibility 

47# I don't know if external plugin code depends on it existing 

48# We no longer do 

49MODENAMES = ImageMode 

50 

51 

52def _get_config(plugin): 

53 """Old Plugin resolution logic. 

54 

55 Remove once we remove the old format manager. 

56 """ 

57 

58 extension_name = None 

59 

60 if Path(plugin).suffix.lower() in known_extensions: 

61 extension_name = Path(plugin).suffix.lower() 

62 elif plugin in known_plugins: 

63 pass 

64 elif plugin.lower() in known_extensions: 

65 extension_name = plugin.lower() 

66 elif "." + plugin.lower() in known_extensions: 

67 extension_name = "." + plugin.lower() 

68 else: 

69 raise IndexError(f"No format known by name `{plugin}`.") 

70 

71 if extension_name is not None: 

72 for plugin_name in [ 

73 x 

74 for file_extension in known_extensions[extension_name] 

75 for x in file_extension.priority 

76 ]: 

77 if known_plugins[plugin_name].is_legacy: 

78 plugin = plugin_name 

79 break 

80 

81 return known_plugins[plugin] 

82 

83 

84class Format(object): 

85 """Represents an implementation to read/write a particular file format 

86 

87 A format instance is responsible for 1) providing information about 

88 a format; 2) determining whether a certain file can be read/written 

89 with this format; 3) providing a reader/writer class. 

90 

91 Generally, imageio will select the right format and use that to 

92 read/write an image. A format can also be explicitly chosen in all 

93 read/write functions. Use ``print(format)``, or ``help(format_name)`` 

94 to see its documentation. 

95 

96 To implement a specific format, one should create a subclass of 

97 Format and the Format.Reader and Format.Writer classes. See 

98 :class:`imageio.plugins` for details. 

99 

100 Parameters 

101 ---------- 

102 name : str 

103 A short name of this format. Users can select a format using its name. 

104 description : str 

105 A one-line description of the format. 

106 extensions : str | list | None 

107 List of filename extensions that this format supports. If a 

108 string is passed it should be space or comma separated. The 

109 extensions are used in the documentation and to allow users to 

110 select a format by file extension. It is not used to determine 

111 what format to use for reading/saving a file. 

112 modes : str 

113 A string containing the modes that this format can handle ('iIvV'), 

114 “i” for an image, “I” for multiple images, “v” for a volume, 

115 “V” for multiple volumes. 

116 This attribute is used in the documentation and to select the 

117 formats when reading/saving a file. 

118 """ 

119 

120 def __init__(self, name, description, extensions=None, modes=None): 

121 """Initialize the Plugin. 

122 

123 Parameters 

124 ---------- 

125 name : str 

126 A short name of this format. Users can select a format using its name. 

127 description : str 

128 A one-line description of the format. 

129 extensions : str | list | None 

130 List of filename extensions that this format supports. If a 

131 string is passed it should be space or comma separated. The 

132 extensions are used in the documentation and to allow users to 

133 select a format by file extension. It is not used to determine 

134 what format to use for reading/saving a file. 

135 modes : str 

136 A string containing the modes that this format can handle ('iIvV'), 

137 “i” for an image, “I” for multiple images, “v” for a volume, 

138 “V” for multiple volumes. 

139 This attribute is used in the documentation and to select the 

140 formats when reading/saving a file. 

141 """ 

142 

143 # Store name and description 

144 self._name = name.upper() 

145 self._description = description 

146 

147 # Store extensions, do some effort to normalize them. 

148 # They are stored as a list of lowercase strings without leading dots. 

149 if extensions is None: 

150 extensions = [] 

151 elif isinstance(extensions, str): 

152 extensions = extensions.replace(",", " ").split(" ") 

153 # 

154 if isinstance(extensions, (tuple, list)): 

155 self._extensions = tuple( 

156 ["." + e.strip(".").lower() for e in extensions if e] 

157 ) 

158 else: 

159 raise ValueError("Invalid value for extensions given.") 

160 

161 # Store mode 

162 self._modes = modes or "" 

163 if not isinstance(self._modes, str): 

164 raise ValueError("Invalid value for modes given.") 

165 for m in self._modes: 

166 if m not in "iIvV?": 

167 raise ValueError("Invalid value for mode given.") 

168 

169 def __repr__(self): 

170 # Short description 

171 return "<Format %s - %s>" % (self.name, self.description) 

172 

173 def __str__(self): 

174 return self.doc 

175 

176 @property 

177 def doc(self): 

178 """The documentation for this format (name + description + docstring).""" 

179 # Our docsring is assumed to be indented by four spaces. The 

180 # first line needs special attention. 

181 return "%s - %s\n\n %s\n" % ( 

182 self.name, 

183 self.description, 

184 self.__doc__.strip(), 

185 ) 

186 

187 @property 

188 def name(self): 

189 """The name of this format.""" 

190 return self._name 

191 

192 @property 

193 def description(self): 

194 """A short description of this format.""" 

195 return self._description 

196 

197 @property 

198 def extensions(self): 

199 """A list of file extensions supported by this plugin. 

200 These are all lowercase with a leading dot. 

201 """ 

202 return self._extensions 

203 

204 @property 

205 def modes(self): 

206 """A string specifying the modes that this format can handle.""" 

207 return self._modes 

208 

209 def get_reader(self, request): 

210 """get_reader(request) 

211 

212 Return a reader object that can be used to read data and info 

213 from the given file. Users are encouraged to use 

214 imageio.get_reader() instead. 

215 """ 

216 select_mode = request.mode[1] if request.mode[1] in "iIvV" else "" 

217 if select_mode not in self.modes: 

218 raise RuntimeError( 

219 f"Format {self.name} cannot read in {request.mode.image_mode} mode" 

220 ) 

221 return self.Reader(self, request) 

222 

223 def get_writer(self, request): 

224 """get_writer(request) 

225 

226 Return a writer object that can be used to write data and info 

227 to the given file. Users are encouraged to use 

228 imageio.get_writer() instead. 

229 """ 

230 select_mode = request.mode[1] if request.mode[1] in "iIvV" else "" 

231 if select_mode not in self.modes: 

232 raise RuntimeError( 

233 f"Format {self.name} cannot write in {request.mode.image_mode} mode" 

234 ) 

235 return self.Writer(self, request) 

236 

237 def can_read(self, request): 

238 """can_read(request) 

239 

240 Get whether this format can read data from the specified uri. 

241 """ 

242 return self._can_read(request) 

243 

244 def can_write(self, request): 

245 """can_write(request) 

246 

247 Get whether this format can write data to the speciefed uri. 

248 """ 

249 return self._can_write(request) 

250 

251 def _can_read(self, request): # pragma: no cover 

252 """Check if Plugin can read from ImageResource. 

253 

254 This method is called when the format manager is searching for a format 

255 to read a certain image. Return True if this format can do it. 

256 

257 The format manager is aware of the extensions and the modes that each 

258 format can handle. It will first ask all formats that *seem* to be able 

259 to read it whether they can. If none can, it will ask the remaining 

260 formats if they can: the extension might be missing, and this allows 

261 formats to provide functionality for certain extensions, while giving 

262 preference to other plugins. 

263 

264 If a format says it can, it should live up to it. The format would 

265 ideally check the request.firstbytes and look for a header of some kind. 

266 

267 Parameters 

268 ---------- 

269 request : Request 

270 A request that can be used to access the ImageResource and obtain 

271 metadata about it. 

272 

273 Returns 

274 ------- 

275 can_read : bool 

276 True if the plugin can read from the ImageResource, False otherwise. 

277 

278 """ 

279 return None # Plugins must implement this 

280 

281 def _can_write(self, request): # pragma: no cover 

282 """Check if Plugin can write to ImageResource. 

283 

284 Parameters 

285 ---------- 

286 request : Request 

287 A request that can be used to access the ImageResource and obtain 

288 metadata about it. 

289 

290 Returns 

291 ------- 

292 can_read : bool 

293 True if the plugin can write to the ImageResource, False otherwise. 

294 

295 """ 

296 return None # Plugins must implement this 

297 

298 # ----- 

299 

300 class _BaseReaderWriter(object): 

301 """Base class for the Reader and Writer class to implement common 

302 functionality. It implements a similar approach for opening/closing 

303 and context management as Python's file objects. 

304 """ 

305 

306 def __init__(self, format, request): 

307 self.__closed = False 

308 self._BaseReaderWriter_last_index = -1 

309 self._format = format 

310 self._request = request 

311 # Open the reader/writer 

312 self._open(**self.request.kwargs.copy()) 

313 

314 @property 

315 def format(self): 

316 """The :class:`.Format` object corresponding to the current 

317 read/write operation. 

318 """ 

319 return self._format 

320 

321 @property 

322 def request(self): 

323 """The :class:`.Request` object corresponding to the 

324 current read/write operation. 

325 """ 

326 return self._request 

327 

328 def __enter__(self): 

329 self._checkClosed() 

330 return self 

331 

332 def __exit__(self, type, value, traceback): 

333 if value is None: 

334 # Otherwise error in close hide the real error. 

335 self.close() 

336 

337 def __del__(self): 

338 try: 

339 self.close() 

340 except Exception: # pragma: no cover 

341 pass # Suppress noise when called during interpreter shutdown 

342 

343 def close(self): 

344 """Flush and close the reader/writer. 

345 This method has no effect if it is already closed. 

346 """ 

347 if self.__closed: 

348 return 

349 self.__closed = True 

350 self._close() 

351 # Process results and clean request object 

352 self.request.finish() 

353 

354 @property 

355 def closed(self): 

356 """Whether the reader/writer is closed.""" 

357 return self.__closed 

358 

359 def _checkClosed(self, msg=None): 

360 """Internal: raise an ValueError if reader/writer is closed""" 

361 if self.closed: 

362 what = self.__class__.__name__ 

363 msg = msg or ("I/O operation on closed %s." % what) 

364 raise RuntimeError(msg) 

365 

366 # To implement 

367 

368 def _open(self, **kwargs): 

369 """_open(**kwargs) 

370 

371 Plugins should probably implement this. 

372 

373 It is called when reader/writer is created. Here the 

374 plugin can do its initialization. The given keyword arguments 

375 are those that were given by the user at imageio.read() or 

376 imageio.write(). 

377 """ 

378 raise NotImplementedError() 

379 

380 def _close(self): 

381 """_close() 

382 

383 Plugins should probably implement this. 

384 

385 It is called when the reader/writer is closed. Here the plugin 

386 can do a cleanup, flush, etc. 

387 

388 """ 

389 raise NotImplementedError() 

390 

391 # ----- 

392 

393 class Reader(_BaseReaderWriter): 

394 """ 

395 The purpose of a reader object is to read data from an image 

396 resource, and should be obtained by calling :func:`.get_reader`. 

397 

398 A reader can be used as an iterator to read multiple images, 

399 and (if the format permits) only reads data from the file when 

400 new data is requested (i.e. streaming). A reader can also be 

401 used as a context manager so that it is automatically closed. 

402 

403 Plugins implement Reader's for different formats. Though rare, 

404 plugins may provide additional functionality (beyond what is 

405 provided by the base reader class). 

406 """ 

407 

408 def get_length(self): 

409 """get_length() 

410 

411 Get the number of images in the file. (Note: you can also 

412 use ``len(reader_object)``.) 

413 

414 The result can be: 

415 * 0 for files that only have meta data 

416 * 1 for singleton images (e.g. in PNG, JPEG, etc.) 

417 * N for image series 

418 * inf for streams (series of unknown length) 

419 """ 

420 return self._get_length() 

421 

422 def get_data(self, index, **kwargs): 

423 """get_data(index, **kwargs) 

424 

425 Read image data from the file, using the image index. The 

426 returned image has a 'meta' attribute with the meta data. 

427 Raises IndexError if the index is out of range. 

428 

429 Some formats may support additional keyword arguments. These are 

430 listed in the documentation of those formats. 

431 """ 

432 self._checkClosed() 

433 self._BaseReaderWriter_last_index = index 

434 try: 

435 im, meta = self._get_data(index, **kwargs) 

436 except StopIteration: 

437 raise IndexError(index) 

438 return Array(im, meta) # Array tests im and meta 

439 

440 def get_next_data(self, **kwargs): 

441 """get_next_data(**kwargs) 

442 

443 Read the next image from the series. 

444 

445 Some formats may support additional keyword arguments. These are 

446 listed in the documentation of those formats. 

447 """ 

448 return self.get_data(self._BaseReaderWriter_last_index + 1, **kwargs) 

449 

450 def set_image_index(self, index, **kwargs): 

451 """set_image_index(index) 

452 

453 Set the internal pointer such that the next call to 

454 get_next_data() returns the image specified by the index 

455 """ 

456 self._checkClosed() 

457 n = self.get_length() 

458 self._BaseReaderWriter_last_index = min(max(index - 1, -1), n) 

459 

460 def get_meta_data(self, index=None): 

461 """get_meta_data(index=None) 

462 

463 Read meta data from the file. using the image index. If the 

464 index is omitted or None, return the file's (global) meta data. 

465 

466 Note that ``get_data`` also provides the meta data for the returned 

467 image as an attribute of that image. 

468 

469 The meta data is a dict, which shape depends on the format. 

470 E.g. for JPEG, the dict maps group names to subdicts and each 

471 group is a dict with name-value pairs. The groups represent 

472 the different metadata formats (EXIF, XMP, etc.). 

473 """ 

474 self._checkClosed() 

475 meta = self._get_meta_data(index) 

476 if not isinstance(meta, dict): 

477 raise ValueError( 

478 "Meta data must be a dict, not %r" % meta.__class__.__name__ 

479 ) 

480 return meta 

481 

482 def iter_data(self): 

483 """iter_data() 

484 

485 Iterate over all images in the series. (Note: you can also 

486 iterate over the reader object.) 

487 

488 """ 

489 self._checkClosed() 

490 n = self.get_length() 

491 i = 0 

492 while i < n: 

493 try: 

494 im, meta = self._get_data(i) 

495 except StopIteration: 

496 return 

497 except IndexError: 

498 if n == float("inf"): 

499 return 

500 raise 

501 yield Array(im, meta) 

502 i += 1 

503 

504 # Compatibility 

505 

506 def __iter__(self): 

507 return self.iter_data() 

508 

509 def __len__(self): 

510 n = self.get_length() 

511 if n == float("inf"): 

512 n = sys.maxsize 

513 return n 

514 

515 # To implement 

516 

517 def _get_length(self): 

518 """_get_length() 

519 

520 Plugins must implement this. 

521 

522 The returned scalar specifies the number of images in the series. 

523 See Reader.get_length for more information. 

524 """ 

525 raise NotImplementedError() 

526 

527 def _get_data(self, index): 

528 """_get_data() 

529 

530 Plugins must implement this, but may raise an IndexError in 

531 case the plugin does not support random access. 

532 

533 It should return the image and meta data: (ndarray, dict). 

534 """ 

535 raise NotImplementedError() 

536 

537 def _get_meta_data(self, index): 

538 """_get_meta_data(index) 

539 

540 Plugins must implement this. 

541 

542 It should return the meta data as a dict, corresponding to the 

543 given index, or to the file's (global) meta data if index is 

544 None. 

545 """ 

546 raise NotImplementedError() 

547 

548 # ----- 

549 

550 class Writer(_BaseReaderWriter): 

551 """ 

552 The purpose of a writer object is to write data to an image 

553 resource, and should be obtained by calling :func:`.get_writer`. 

554 

555 A writer will (if the format permits) write data to the file 

556 as soon as new data is provided (i.e. streaming). A writer can 

557 also be used as a context manager so that it is automatically 

558 closed. 

559 

560 Plugins implement Writer's for different formats. Though rare, 

561 plugins may provide additional functionality (beyond what is 

562 provided by the base writer class). 

563 """ 

564 

565 def append_data(self, im, meta=None): 

566 """append_data(im, meta={}) 

567 

568 Append an image (and meta data) to the file. The final meta 

569 data that is used consists of the meta data on the given 

570 image (if applicable), updated with the given meta data. 

571 """ 

572 self._checkClosed() 

573 # Check image data 

574 if not isinstance(im, np.ndarray): 

575 raise ValueError("append_data requires ndarray as first arg") 

576 # Get total meta dict 

577 total_meta = {} 

578 if hasattr(im, "meta") and isinstance(im.meta, dict): 

579 total_meta.update(im.meta) 

580 if meta is None: 

581 pass 

582 elif not isinstance(meta, dict): 

583 raise ValueError("Meta must be a dict.") 

584 else: 

585 total_meta.update(meta) 

586 

587 # Decouple meta info 

588 im = asarray(im) 

589 # Call 

590 return self._append_data(im, total_meta) 

591 

592 def set_meta_data(self, meta): 

593 """set_meta_data(meta) 

594 

595 Sets the file's (global) meta data. The meta data is a dict which 

596 shape depends on the format. E.g. for JPEG the dict maps 

597 group names to subdicts, and each group is a dict with 

598 name-value pairs. The groups represents the different 

599 metadata formats (EXIF, XMP, etc.). 

600 

601 Note that some meta formats may not be supported for 

602 writing, and individual fields may be ignored without 

603 warning if they are invalid. 

604 """ 

605 self._checkClosed() 

606 if not isinstance(meta, dict): 

607 raise ValueError("Meta must be a dict.") 

608 else: 

609 return self._set_meta_data(meta) 

610 

611 # To implement 

612 

613 def _append_data(self, im, meta): 

614 # Plugins must implement this 

615 raise NotImplementedError() 

616 

617 def _set_meta_data(self, meta): 

618 # Plugins must implement this 

619 raise NotImplementedError() 

620 

621 

622class FormatManager(object): 

623 """ 

624 The FormatManager is a singleton plugin factory. 

625 

626 The format manager supports getting a format object using indexing (by 

627 format name or extension). When used as an iterator, this object 

628 yields all registered format objects. 

629 

630 See also :func:`.help`. 

631 """ 

632 

633 @property 

634 def _formats(self): 

635 available_formats = list() 

636 

637 for config in known_plugins.values(): 

638 with contextlib.suppress(ImportError): 

639 # if an exception is raised, then format not installed 

640 if config.is_legacy and config.format is not None: 

641 available_formats.append(config) 

642 

643 return available_formats 

644 

645 def __repr__(self): 

646 return f"<imageio.FormatManager with {len(self._formats)} registered formats>" 

647 

648 def __iter__(self): 

649 return iter(x.format for x in self._formats) 

650 

651 def __len__(self): 

652 return len(self._formats) 

653 

654 def __str__(self): 

655 ss = [] 

656 for config in self._formats: 

657 ext = config.legacy_args["extensions"] 

658 desc = config.legacy_args["description"] 

659 s = f"{config.name} - {desc} [{ext}]" 

660 ss.append(s) 

661 return "\n".join(ss) 

662 

663 def __getitem__(self, name): 

664 warnings.warn( 

665 "The usage of `FormatManager` is deprecated and it will be " 

666 "removed in Imageio v3. Use `iio.imopen` instead.", 

667 DeprecationWarning, 

668 stacklevel=2, 

669 ) 

670 

671 if not isinstance(name, str): 

672 raise ValueError( 

673 "Looking up a format should be done by name or by extension." 

674 ) 

675 

676 if name == "": 

677 raise ValueError("No format matches the empty string.") 

678 

679 # Test if name is existing file 

680 if Path(name).is_file(): 

681 # legacy compatibility - why test reading here?? 

682 try: 

683 return imopen(name, "r", legacy_mode=True)._format 

684 except ValueError: 

685 # no plugin can read the file 

686 pass 

687 

688 config = _get_config(name.upper()) 

689 

690 try: 

691 return config.format 

692 except ImportError: 

693 raise ImportError( 

694 f"The `{config.name}` format is not installed. " 

695 f"Use `pip install imageio[{config.install_name}]` to install it." 

696 ) 

697 

698 def sort(self, *names): 

699 """sort(name1, name2, name3, ...) 

700 

701 Sort the formats based on zero or more given names; a format with 

702 a name that matches one of the given names will take precedence 

703 over other formats. A match means an equal name, or ending with 

704 that name (though the former counts higher). Case insensitive. 

705 

706 Format preference will match the order of the given names: using 

707 ``sort('TIFF', '-FI', '-PIL')`` would prefer the FreeImage formats 

708 over the Pillow formats, but prefer TIFF even more. Each time 

709 this is called, the starting point is the default format order, 

710 and calling ``sort()`` with no arguments will reset the order. 

711 

712 Be aware that using the function can affect the behavior of 

713 other code that makes use of imageio. 

714 

715 Also see the ``IMAGEIO_FORMAT_ORDER`` environment variable. 

716 """ 

717 

718 warnings.warn( 

719 "`FormatManager` is deprecated and it will be removed in ImageIO v3." 

720 " Migrating `FormatManager.sort` depends on your use-case:\n" 

721 "\t- modify `iio.config.known_plugins` to specify the search order for " 

722 "unrecognized formats.\n" 

723 "\t- modify `iio.config.known_extensions[<extension>].priority`" 

724 " to control a specific extension.", 

725 DeprecationWarning, 

726 stacklevel=2, 

727 ) 

728 

729 # Check and sanitize input 

730 for name in names: 

731 if not isinstance(name, str): 

732 raise TypeError("formats.sort() accepts only string names.") 

733 if any(c in name for c in ".,"): 

734 raise ValueError( 

735 "Names given to formats.sort() should not " 

736 "contain dots `.` or commas `,`." 

737 ) 

738 

739 should_reset = len(names) == 0 

740 if should_reset: 

741 names = _original_order 

742 

743 sane_names = [name.strip().upper() for name in names if name != ""] 

744 

745 # enforce order for every extension that uses it 

746 flat_extensions = [ 

747 ext for ext_list in known_extensions.values() for ext in ext_list 

748 ] 

749 for extension in flat_extensions: 

750 if should_reset: 

751 extension.reset() 

752 continue 

753 

754 for name in reversed(sane_names): 

755 for plugin in [x for x in extension.default_priority]: 

756 if plugin.endswith(name): 

757 extension.priority.remove(plugin) 

758 extension.priority.insert(0, plugin) 

759 

760 old_order = known_plugins.copy() 

761 known_plugins.clear() 

762 

763 for name in sane_names: 

764 plugin = old_order.pop(name, None) 

765 if plugin is not None: 

766 known_plugins[name] = plugin 

767 

768 known_plugins.update(old_order) 

769 

770 def add_format(self, iio_format, overwrite=False): 

771 """add_format(format, overwrite=False) 

772 

773 Register a format, so that imageio can use it. If a format with the 

774 same name already exists, an error is raised, unless overwrite is True, 

775 in which case the current format is replaced. 

776 """ 

777 

778 warnings.warn( 

779 "`FormatManager` is deprecated and it will be removed in ImageIO v3." 

780 "To migrate `FormatManager.add_format` add the plugin directly to " 

781 "`iio.config.known_plugins`.", 

782 DeprecationWarning, 

783 stacklevel=2, 

784 ) 

785 

786 if not isinstance(iio_format, Format): 

787 raise ValueError("add_format needs argument to be a Format object") 

788 elif not overwrite and iio_format.name in self.get_format_names(): 

789 raise ValueError( 

790 f"A Format named {iio_format.name} is already registered, use" 

791 " `overwrite=True` to replace." 

792 ) 

793 

794 config = PluginConfig( 

795 name=iio_format.name.upper(), 

796 class_name=iio_format.__class__.__name__, 

797 module_name=iio_format.__class__.__module__, 

798 is_legacy=True, 

799 install_name="unknown", 

800 legacy_args={ 

801 "name": iio_format.name, 

802 "description": iio_format.description, 

803 "extensions": " ".join(iio_format.extensions), 

804 "modes": iio_format.modes, 

805 }, 

806 ) 

807 

808 known_plugins[config.name] = config 

809 

810 for extension in iio_format.extensions: 

811 # be conservative and always treat it as a unique file format 

812 ext = FileExtension( 

813 extension=extension, 

814 priority=[config.name], 

815 name="Unique Format", 

816 description="A format inserted at runtime." 

817 f" It is being read by the `{config.name}` plugin.", 

818 ) 

819 known_extensions.setdefault(extension, list()).append(ext) 

820 

821 def search_read_format(self, request): 

822 """search_read_format(request) 

823 

824 Search a format that can read a file according to the given request. 

825 Returns None if no appropriate format was found. (used internally) 

826 """ 

827 

828 try: 

829 # in legacy_mode imopen returns a LegacyPlugin 

830 return imopen(request, request.mode.io_mode, legacy_mode=True)._format 

831 except AttributeError: 

832 warnings.warn( 

833 "ImageIO now uses a v3 plugin when reading this format." 

834 " Please migrate to the v3 API (preferred) or use imageio.v2.", 

835 DeprecationWarning, 

836 stacklevel=2, 

837 ) 

838 return None 

839 except ValueError: 

840 # no plugin can read this request 

841 # but the legacy API doesn't raise 

842 return None 

843 

844 def search_write_format(self, request): 

845 """search_write_format(request) 

846 

847 Search a format that can write a file according to the given request. 

848 Returns None if no appropriate format was found. (used internally) 

849 """ 

850 

851 try: 

852 # in legacy_mode imopen returns a LegacyPlugin 

853 return imopen(request, request.mode.io_mode, legacy_mode=True)._format 

854 except AttributeError: 

855 warnings.warn( 

856 "ImageIO now uses a v3 plugin when writing this format." 

857 " Please migrate to the v3 API (preferred) or use imageio.v2.", 

858 DeprecationWarning, 

859 stacklevel=2, 

860 ) 

861 return None 

862 except ValueError: 

863 # no plugin can write this request 

864 # but the legacy API doesn't raise 

865 return None 

866 

867 def get_format_names(self): 

868 """Get the names of all registered formats.""" 

869 

870 warnings.warn( 

871 "`FormatManager` is deprecated and it will be removed in ImageIO v3." 

872 "To migrate `FormatManager.get_format_names` use `iio.config.known_plugins.keys()` instead.", 

873 DeprecationWarning, 

874 stacklevel=2, 

875 ) 

876 

877 return [f.name for f in self._formats] 

878 

879 def show(self): 

880 """Show a nicely formatted list of available formats""" 

881 print(self)