Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/PIL/features.py: 20%

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

177 statements  

1from __future__ import annotations 

2 

3import collections 

4import os 

5import sys 

6import warnings 

7from typing import IO 

8 

9import PIL 

10 

11from . import Image 

12from ._deprecate import deprecate 

13 

14modules = { 

15 "pil": ("PIL._imaging", "PILLOW_VERSION"), 

16 "tkinter": ("PIL._tkinter_finder", "tk_version"), 

17 "freetype2": ("PIL._imagingft", "freetype2_version"), 

18 "littlecms2": ("PIL._imagingcms", "littlecms_version"), 

19 "webp": ("PIL._webp", "webpdecoder_version"), 

20 "avif": ("PIL._avif", "libavif_version"), 

21} 

22 

23 

24def check_module(feature: str) -> bool: 

25 """ 

26 Checks if a module is available. 

27 

28 :param feature: The module to check for. 

29 :returns: ``True`` if available, ``False`` otherwise. 

30 :raises ValueError: If the module is not defined in this version of Pillow. 

31 """ 

32 if feature not in modules: 

33 msg = f"Unknown module {feature}" 

34 raise ValueError(msg) 

35 

36 module, ver = modules[feature] 

37 

38 try: 

39 __import__(module) 

40 return True 

41 except ModuleNotFoundError: 

42 return False 

43 except ImportError as ex: 

44 warnings.warn(str(ex)) 

45 return False 

46 

47 

48def version_module(feature: str) -> str | None: 

49 """ 

50 :param feature: The module to check for. 

51 :returns: 

52 The loaded version number as a string, or ``None`` if unknown or not available. 

53 :raises ValueError: If the module is not defined in this version of Pillow. 

54 """ 

55 if not check_module(feature): 

56 return None 

57 

58 module, ver = modules[feature] 

59 

60 return getattr(__import__(module, fromlist=[ver]), ver) 

61 

62 

63def get_supported_modules() -> list[str]: 

64 """ 

65 :returns: A list of all supported modules. 

66 """ 

67 return [f for f in modules if check_module(f)] 

68 

69 

70codecs = { 

71 "jpg": ("jpeg", "jpeglib"), 

72 "jpg_2000": ("jpeg2k", "jp2klib"), 

73 "zlib": ("zip", "zlib"), 

74 "libtiff": ("libtiff", "libtiff"), 

75} 

76 

77 

78def check_codec(feature: str) -> bool: 

79 """ 

80 Checks if a codec is available. 

81 

82 :param feature: The codec to check for. 

83 :returns: ``True`` if available, ``False`` otherwise. 

84 :raises ValueError: If the codec is not defined in this version of Pillow. 

85 """ 

86 if feature not in codecs: 

87 msg = f"Unknown codec {feature}" 

88 raise ValueError(msg) 

89 

90 codec, lib = codecs[feature] 

91 

92 return f"{codec}_encoder" in dir(Image.core) 

93 

94 

95def version_codec(feature: str) -> str | None: 

96 """ 

97 :param feature: The codec to check for. 

98 :returns: 

99 The version number as a string, or ``None`` if not available. 

100 Checked at compile time for ``jpg``, run-time otherwise. 

101 :raises ValueError: If the codec is not defined in this version of Pillow. 

102 """ 

103 if not check_codec(feature): 

104 return None 

105 

106 codec, lib = codecs[feature] 

107 

108 version = getattr(Image.core, f"{lib}_version") 

109 

110 if feature == "libtiff": 

111 return version.split("\n")[0].split("Version ")[1] 

112 

113 return version 

114 

115 

116def get_supported_codecs() -> list[str]: 

117 """ 

118 :returns: A list of all supported codecs. 

119 """ 

120 return [f for f in codecs if check_codec(f)] 

121 

122 

123features: dict[str, tuple[str, str | bool, str | None]] = { 

124 "webp_anim": ("PIL._webp", True, None), 

125 "webp_mux": ("PIL._webp", True, None), 

126 "transp_webp": ("PIL._webp", True, None), 

127 "raqm": ("PIL._imagingft", "HAVE_RAQM", "raqm_version"), 

128 "fribidi": ("PIL._imagingft", "HAVE_FRIBIDI", "fribidi_version"), 

129 "harfbuzz": ("PIL._imagingft", "HAVE_HARFBUZZ", "harfbuzz_version"), 

130 "libjpeg_turbo": ("PIL._imaging", "HAVE_LIBJPEGTURBO", "libjpeg_turbo_version"), 

131 "mozjpeg": ("PIL._imaging", "HAVE_MOZJPEG", "libjpeg_turbo_version"), 

132 "zlib_ng": ("PIL._imaging", "HAVE_ZLIBNG", "zlib_ng_version"), 

133 "libimagequant": ("PIL._imaging", "HAVE_LIBIMAGEQUANT", "imagequant_version"), 

134 "xcb": ("PIL._imaging", "HAVE_XCB", None), 

135} 

136 

137 

138def check_feature(feature: str) -> bool | None: 

139 """ 

140 Checks if a feature is available. 

141 

142 :param feature: The feature to check for. 

143 :returns: ``True`` if available, ``False`` if unavailable, ``None`` if unknown. 

144 :raises ValueError: If the feature is not defined in this version of Pillow. 

145 """ 

146 if feature not in features: 

147 msg = f"Unknown feature {feature}" 

148 raise ValueError(msg) 

149 

150 module, flag, ver = features[feature] 

151 

152 if isinstance(flag, bool): 

153 deprecate(f'check_feature("{feature}")', 12) 

154 try: 

155 imported_module = __import__(module, fromlist=["PIL"]) 

156 if isinstance(flag, bool): 

157 return flag 

158 return getattr(imported_module, flag) 

159 except ModuleNotFoundError: 

160 return None 

161 except ImportError as ex: 

162 warnings.warn(str(ex)) 

163 return None 

164 

165 

166def version_feature(feature: str) -> str | None: 

167 """ 

168 :param feature: The feature to check for. 

169 :returns: The version number as a string, or ``None`` if not available. 

170 :raises ValueError: If the feature is not defined in this version of Pillow. 

171 """ 

172 if not check_feature(feature): 

173 return None 

174 

175 module, flag, ver = features[feature] 

176 

177 if ver is None: 

178 return None 

179 

180 return getattr(__import__(module, fromlist=[ver]), ver) 

181 

182 

183def get_supported_features() -> list[str]: 

184 """ 

185 :returns: A list of all supported features. 

186 """ 

187 supported_features = [] 

188 for f, (module, flag, _) in features.items(): 

189 if flag is True: 

190 for feature, (feature_module, _) in modules.items(): 

191 if feature_module == module: 

192 if check_module(feature): 

193 supported_features.append(f) 

194 break 

195 elif check_feature(f): 

196 supported_features.append(f) 

197 return supported_features 

198 

199 

200def check(feature: str) -> bool | None: 

201 """ 

202 :param feature: A module, codec, or feature name. 

203 :returns: 

204 ``True`` if the module, codec, or feature is available, 

205 ``False`` or ``None`` otherwise. 

206 """ 

207 

208 if feature in modules: 

209 return check_module(feature) 

210 if feature in codecs: 

211 return check_codec(feature) 

212 if feature in features: 

213 return check_feature(feature) 

214 warnings.warn(f"Unknown feature '{feature}'.", stacklevel=2) 

215 return False 

216 

217 

218def version(feature: str) -> str | None: 

219 """ 

220 :param feature: 

221 The module, codec, or feature to check for. 

222 :returns: 

223 The version number as a string, or ``None`` if unknown or not available. 

224 """ 

225 if feature in modules: 

226 return version_module(feature) 

227 if feature in codecs: 

228 return version_codec(feature) 

229 if feature in features: 

230 return version_feature(feature) 

231 return None 

232 

233 

234def get_supported() -> list[str]: 

235 """ 

236 :returns: A list of all supported modules, features, and codecs. 

237 """ 

238 

239 ret = get_supported_modules() 

240 ret.extend(get_supported_features()) 

241 ret.extend(get_supported_codecs()) 

242 return ret 

243 

244 

245def pilinfo(out: IO[str] | None = None, supported_formats: bool = True) -> None: 

246 """ 

247 Prints information about this installation of Pillow. 

248 This function can be called with ``python3 -m PIL``. 

249 It can also be called with ``python3 -m PIL.report`` or ``python3 -m PIL --report`` 

250 to have "supported_formats" set to ``False``, omitting the list of all supported 

251 image file formats. 

252 

253 :param out: 

254 The output stream to print to. Defaults to ``sys.stdout`` if ``None``. 

255 :param supported_formats: 

256 If ``True``, a list of all supported image file formats will be printed. 

257 """ 

258 

259 if out is None: 

260 out = sys.stdout 

261 

262 Image.init() 

263 

264 print("-" * 68, file=out) 

265 print(f"Pillow {PIL.__version__}", file=out) 

266 py_version_lines = sys.version.splitlines() 

267 print(f"Python {py_version_lines[0].strip()}", file=out) 

268 for py_version in py_version_lines[1:]: 

269 print(f" {py_version.strip()}", file=out) 

270 print("-" * 68, file=out) 

271 print(f"Python executable is {sys.executable or 'unknown'}", file=out) 

272 if sys.prefix != sys.base_prefix: 

273 print(f"Environment Python files loaded from {sys.prefix}", file=out) 

274 print(f"System Python files loaded from {sys.base_prefix}", file=out) 

275 print("-" * 68, file=out) 

276 print( 

277 f"Python Pillow modules loaded from {os.path.dirname(Image.__file__)}", 

278 file=out, 

279 ) 

280 print( 

281 f"Binary Pillow modules loaded from {os.path.dirname(Image.core.__file__)}", 

282 file=out, 

283 ) 

284 print("-" * 68, file=out) 

285 

286 for name, feature in [ 

287 ("pil", "PIL CORE"), 

288 ("tkinter", "TKINTER"), 

289 ("freetype2", "FREETYPE2"), 

290 ("littlecms2", "LITTLECMS2"), 

291 ("webp", "WEBP"), 

292 ("avif", "AVIF"), 

293 ("jpg", "JPEG"), 

294 ("jpg_2000", "OPENJPEG (JPEG2000)"), 

295 ("zlib", "ZLIB (PNG/ZIP)"), 

296 ("libtiff", "LIBTIFF"), 

297 ("raqm", "RAQM (Bidirectional Text)"), 

298 ("libimagequant", "LIBIMAGEQUANT (Quantization method)"), 

299 ("xcb", "XCB (X protocol)"), 

300 ]: 

301 if check(name): 

302 v: str | None = None 

303 if name == "jpg": 

304 libjpeg_turbo_version = version_feature("libjpeg_turbo") 

305 if libjpeg_turbo_version is not None: 

306 v = "mozjpeg" if check_feature("mozjpeg") else "libjpeg-turbo" 

307 v += " " + libjpeg_turbo_version 

308 if v is None: 

309 v = version(name) 

310 if v is not None: 

311 version_static = name in ("pil", "jpg") 

312 if name == "littlecms2": 

313 # this check is also in src/_imagingcms.c:setup_module() 

314 version_static = tuple(int(x) for x in v.split(".")) < (2, 7) 

315 t = "compiled for" if version_static else "loaded" 

316 if name == "zlib": 

317 zlib_ng_version = version_feature("zlib_ng") 

318 if zlib_ng_version is not None: 

319 v += ", compiled for zlib-ng " + zlib_ng_version 

320 elif name == "raqm": 

321 for f in ("fribidi", "harfbuzz"): 

322 v2 = version_feature(f) 

323 if v2 is not None: 

324 v += f", {f} {v2}" 

325 print("---", feature, "support ok,", t, v, file=out) 

326 else: 

327 print("---", feature, "support ok", file=out) 

328 else: 

329 print("***", feature, "support not installed", file=out) 

330 print("-" * 68, file=out) 

331 

332 if supported_formats: 

333 extensions = collections.defaultdict(list) 

334 for ext, i in Image.EXTENSION.items(): 

335 extensions[i].append(ext) 

336 

337 for i in sorted(Image.ID): 

338 line = f"{i}" 

339 if i in Image.MIME: 

340 line = f"{line} {Image.MIME[i]}" 

341 print(line, file=out) 

342 

343 if i in extensions: 

344 print( 

345 "Extensions: {}".format(", ".join(sorted(extensions[i]))), file=out 

346 ) 

347 

348 features = [] 

349 if i in Image.OPEN: 

350 features.append("open") 

351 if i in Image.SAVE: 

352 features.append("save") 

353 if i in Image.SAVE_ALL: 

354 features.append("save_all") 

355 if i in Image.DECODERS: 

356 features.append("decode") 

357 if i in Image.ENCODERS: 

358 features.append("encode") 

359 

360 print("Features: {}".format(", ".join(features)), file=out) 

361 print("-" * 68, file=out)