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

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

108 statements  

1from pathlib import Path 

2import warnings 

3 

4from ..config import known_plugins 

5from ..config.extensions import known_extensions 

6from .request import ( 

7 SPECIAL_READ_URIS, 

8 URI_FILENAME, 

9 InitializationError, 

10 IOMode, 

11 Request, 

12) 

13 

14 

15def imopen( 

16 uri, 

17 io_mode, 

18 *, 

19 plugin=None, 

20 extension=None, 

21 format_hint=None, 

22 legacy_mode=False, 

23 **kwargs, 

24): 

25 """Open an ImageResource. 

26 

27 .. warning:: 

28 This warning is for pypy users. If you are not using a context manager, 

29 remember to deconstruct the returned plugin to avoid leaking the file 

30 handle to an unclosed file. 

31 

32 Parameters 

33 ---------- 

34 uri : str or pathlib.Path or bytes or file or Request 

35 The :doc:`ImageResource <../../user_guide/requests>` to load the 

36 image from. 

37 io_mode : str 

38 The mode in which the file is opened. Possible values are:: 

39 

40 ``r`` - open the file for reading 

41 ``w`` - open the file for writing 

42 

43 Depreciated since v2.9: 

44 A second character can be added to give the reader a hint on what 

45 the user expects. This will be ignored by new plugins and will 

46 only have an effect on legacy plugins. Possible values are:: 

47 

48 ``i`` for a single image, 

49 ``I`` for multiple images, 

50 ``v`` for a single volume, 

51 ``V`` for multiple volumes, 

52 ``?`` for don't care 

53 

54 plugin : str, Plugin, or None 

55 The plugin to use. If set to None imopen will perform a 

56 search for a matching plugin. If not None, this takes priority over 

57 the provided format hint. 

58 extension : str 

59 If not None, treat the provided ImageResource as if it had the given 

60 extension. This affects the order in which backends are considered, and 

61 when writing this may also influence the format used when encoding. 

62 format_hint : str 

63 Deprecated. Use `extension` instead. 

64 legacy_mode : bool 

65 If true use the v2 behavior when searching for a suitable 

66 plugin. This will ignore v3 plugins and will check ``plugin`` 

67 against known extensions if no plugin with the given name can be found. 

68 **kwargs : Any 

69 Additional keyword arguments will be passed to the plugin upon 

70 construction. 

71 

72 Notes 

73 ----- 

74 Registered plugins are controlled via the ``known_plugins`` dict in 

75 ``imageio.config``. 

76 

77 Passing a ``Request`` as the uri is only supported if ``legacy_mode`` 

78 is ``True``. In this case ``io_mode`` is ignored. 

79 

80 Using the kwarg ``format_hint`` does not enforce the given format. It merely 

81 provides a `hint` to the selection process and plugin. The selection 

82 processes uses this hint for optimization; however, a plugin's decision how 

83 to read a ImageResource will - typically - still be based on the content of 

84 the resource. 

85 

86 

87 Examples 

88 -------- 

89 

90 >>> import imageio.v3 as iio 

91 >>> with iio.imopen("/path/to/image.png", "r") as file: 

92 >>> im = file.read() 

93 

94 >>> with iio.imopen("/path/to/output.jpg", "w") as file: 

95 >>> file.write(im) 

96 

97 """ 

98 

99 if isinstance(uri, Request) and legacy_mode: 

100 warnings.warn( 

101 "`iio.core.Request` is a low-level object and using it" 

102 " directly as input to `imopen` is discouraged. This will raise" 

103 " an exception in ImageIO v3.", 

104 DeprecationWarning, 

105 stacklevel=2, 

106 ) 

107 

108 request = uri 

109 uri = request.raw_uri 

110 io_mode = request.mode.io_mode 

111 request.format_hint = format_hint 

112 else: 

113 request = Request(uri, io_mode, format_hint=format_hint, extension=extension) 

114 

115 source = "<bytes>" if isinstance(uri, bytes) else uri 

116 

117 # fast-path based on plugin 

118 # (except in legacy mode) 

119 if plugin is not None: 

120 if isinstance(plugin, str): 

121 try: 

122 config = known_plugins[plugin] 

123 except KeyError: 

124 request.finish() 

125 raise ValueError( 

126 f"`{plugin}` is not a registered plugin name." 

127 ) from None 

128 

129 def loader(request, **kwargs): 

130 return config.plugin_class(request, **kwargs) 

131 

132 else: 

133 

134 def loader(request, **kwargs): 

135 return plugin(request, **kwargs) 

136 

137 try: 

138 return loader(request, **kwargs) 

139 except InitializationError as class_specific: 

140 err_from = class_specific 

141 err_type = RuntimeError if legacy_mode else IOError 

142 err_msg = f"`{plugin}` can not handle the given uri." 

143 except ImportError: 

144 err_from = None 

145 err_type = ImportError 

146 err_msg = ( 

147 f"The `{config.name}` plugin is not installed. " 

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

149 ) 

150 except Exception as generic_error: 

151 err_from = generic_error 

152 err_type = IOError 

153 err_msg = f"An unknown error occurred while initializing plugin `{plugin}`." 

154 

155 request.finish() 

156 raise err_type(err_msg) from err_from 

157 

158 # fast-path based on format_hint 

159 if request.format_hint is not None: 

160 for candidate_format in known_extensions[format_hint]: 

161 for plugin_name in candidate_format.priority: 

162 config = known_plugins[plugin_name] 

163 

164 try: 

165 candidate_plugin = config.plugin_class 

166 except ImportError: 

167 # not installed 

168 continue 

169 

170 try: 

171 plugin_instance = candidate_plugin(request, **kwargs) 

172 except InitializationError: 

173 # file extension doesn't match file type 

174 continue 

175 

176 return plugin_instance 

177 else: 

178 resource = ( 

179 "<bytes>" if isinstance(request.raw_uri, bytes) else request.raw_uri 

180 ) 

181 warnings.warn(f"`{resource}` can not be opened as a `{format_hint}` file.") 

182 

183 # fast-path based on file extension 

184 if request.extension in known_extensions: 

185 for candidate_format in known_extensions[request.extension]: 

186 for plugin_name in candidate_format.priority: 

187 config = known_plugins[plugin_name] 

188 

189 try: 

190 candidate_plugin = config.plugin_class 

191 except ImportError: 

192 # not installed 

193 continue 

194 

195 try: 

196 plugin_instance = candidate_plugin(request, **kwargs) 

197 except InitializationError: 

198 # file extension doesn't match file type 

199 continue 

200 

201 return plugin_instance 

202 

203 # error out for read-only special targets 

204 # this is hacky; can we come up with a better solution for this? 

205 if request.mode.io_mode == IOMode.write: 

206 if isinstance(uri, str) and uri.startswith(SPECIAL_READ_URIS): 

207 request.finish() 

208 err_type = ValueError if legacy_mode else IOError 

209 err_msg = f"`{source}` is read-only." 

210 raise err_type(err_msg) 

211 

212 # error out for directories 

213 # this is a bit hacky and should be cleaned once we decide 

214 # how to gracefully handle DICOM 

215 if request._uri_type == URI_FILENAME and Path(request.raw_uri).is_dir(): 

216 request.finish() 

217 err_type = ValueError if legacy_mode else IOError 

218 err_msg = ( 

219 "ImageIO does not generally support reading folders. " 

220 "Limited support may be available via specific plugins. " 

221 "Specify the plugin explicitly using the `plugin` kwarg, e.g. `plugin='DICOM'`" 

222 ) 

223 raise err_type(err_msg) 

224 

225 # close the current request here and use fresh/new ones while trying each 

226 # plugin This is slow (means potentially reopening a resource several 

227 # times), but should only happen rarely because this is the fallback if all 

228 # else fails. 

229 request.finish() 

230 

231 # fallback option: try all plugins 

232 for config in known_plugins.values(): 

233 # each plugin gets its own request 

234 request = Request(uri, io_mode, format_hint=format_hint) 

235 

236 try: 

237 plugin_instance = config.plugin_class(request, **kwargs) 

238 except InitializationError: 

239 continue 

240 except ImportError: 

241 continue 

242 else: 

243 return plugin_instance 

244 

245 err_type = ValueError if legacy_mode else IOError 

246 err_msg = f"Could not find a backend to open `{source}`` with iomode `{io_mode}`." 

247 

248 # check if a missing plugin could help 

249 if request.extension in known_extensions: 

250 missing_plugins = list() 

251 

252 formats = known_extensions[request.extension] 

253 plugin_names = [ 

254 plugin for file_format in formats for plugin in file_format.priority 

255 ] 

256 for name in plugin_names: 

257 config = known_plugins[name] 

258 

259 try: 

260 config.plugin_class 

261 continue 

262 except ImportError: 

263 missing_plugins.append(config) 

264 

265 if len(missing_plugins) > 0: 

266 install_candidates = "\n".join( 

267 [ 

268 ( 

269 f" {config.name}: " 

270 f"pip install imageio[{config.install_name}]" 

271 ) 

272 for config in missing_plugins 

273 ] 

274 ) 

275 err_msg += ( 

276 "\nBased on the extension, the following plugins might add capable backends:\n" 

277 f"{install_candidates}" 

278 ) 

279 

280 request.finish() 

281 raise err_type(err_msg)