Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.10/site-packages/pillow-11.0.0-py3.10-linux-x86_64.egg/PIL/features.py: 16%

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

172 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} 

21 

22 

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

24 """ 

25 Checks if a module is available. 

26 

27 :param feature: The module to check for. 

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

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

30 """ 

31 if feature not in modules: 

32 msg = f"Unknown module {feature}" 

33 raise ValueError(msg) 

34 

35 module, ver = modules[feature] 

36 

37 try: 

38 __import__(module) 

39 return True 

40 except ModuleNotFoundError: 

41 return False 

42 except ImportError as ex: 

43 warnings.warn(str(ex)) 

44 return False 

45 

46 

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

48 """ 

49 :param feature: The module to check for. 

50 :returns: 

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

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

53 """ 

54 if not check_module(feature): 

55 return None 

56 

57 module, ver = modules[feature] 

58 

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

60 

61 

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

63 """ 

64 :returns: A list of all supported modules. 

65 """ 

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

67 

68 

69codecs = { 

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

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

72 "zlib": ("zip", "zlib"), 

73 "libtiff": ("libtiff", "libtiff"), 

74} 

75 

76 

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

78 """ 

79 Checks if a codec is available. 

80 

81 :param feature: The codec to check for. 

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

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

84 """ 

85 if feature not in codecs: 

86 msg = f"Unknown codec {feature}" 

87 raise ValueError(msg) 

88 

89 codec, lib = codecs[feature] 

90 

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

92 

93 

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

95 """ 

96 :param feature: The codec to check for. 

97 :returns: 

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

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

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

101 """ 

102 if not check_codec(feature): 

103 return None 

104 

105 codec, lib = codecs[feature] 

106 

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

108 

109 if feature == "libtiff": 

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

111 

112 return version 

113 

114 

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

116 """ 

117 :returns: A list of all supported codecs. 

118 """ 

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

120 

121 

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

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

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

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

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

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

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

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

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

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

132} 

133 

134 

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

136 """ 

137 Checks if a feature is available. 

138 

139 :param feature: The feature to check for. 

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

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

142 """ 

143 if feature not in features: 

144 msg = f"Unknown feature {feature}" 

145 raise ValueError(msg) 

146 

147 module, flag, ver = features[feature] 

148 

149 if isinstance(flag, bool): 

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

151 try: 

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

153 if isinstance(flag, bool): 

154 return flag 

155 return getattr(imported_module, flag) 

156 except ModuleNotFoundError: 

157 return None 

158 except ImportError as ex: 

159 warnings.warn(str(ex)) 

160 return None 

161 

162 

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

164 """ 

165 :param feature: The feature to check for. 

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

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

168 """ 

169 if not check_feature(feature): 

170 return None 

171 

172 module, flag, ver = features[feature] 

173 

174 if ver is None: 

175 return None 

176 

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

178 

179 

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

181 """ 

182 :returns: A list of all supported features. 

183 """ 

184 supported_features = [] 

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

186 if flag is True: 

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

188 if feature_module == module: 

189 if check_module(feature): 

190 supported_features.append(f) 

191 break 

192 elif check_feature(f): 

193 supported_features.append(f) 

194 return supported_features 

195 

196 

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

198 """ 

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

200 :returns: 

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

202 ``False`` or ``None`` otherwise. 

203 """ 

204 

205 if feature in modules: 

206 return check_module(feature) 

207 if feature in codecs: 

208 return check_codec(feature) 

209 if feature in features: 

210 return check_feature(feature) 

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

212 return False 

213 

214 

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

216 """ 

217 :param feature: 

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

219 :returns: 

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

221 """ 

222 if feature in modules: 

223 return version_module(feature) 

224 if feature in codecs: 

225 return version_codec(feature) 

226 if feature in features: 

227 return version_feature(feature) 

228 return None 

229 

230 

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

232 """ 

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

234 """ 

235 

236 ret = get_supported_modules() 

237 ret.extend(get_supported_features()) 

238 ret.extend(get_supported_codecs()) 

239 return ret 

240 

241 

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

243 """ 

244 Prints information about this installation of Pillow. 

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

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

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

248 image file formats. 

249 

250 :param out: 

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

252 :param supported_formats: 

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

254 """ 

255 

256 if out is None: 

257 out = sys.stdout 

258 

259 Image.init() 

260 

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

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

263 py_version_lines = sys.version.splitlines() 

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

265 for py_version in py_version_lines[1:]: 

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

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

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

269 if sys.prefix != sys.base_prefix: 

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

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

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

273 print( 

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

275 file=out, 

276 ) 

277 print( 

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

279 file=out, 

280 ) 

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

282 

283 for name, feature in [ 

284 ("pil", "PIL CORE"), 

285 ("tkinter", "TKINTER"), 

286 ("freetype2", "FREETYPE2"), 

287 ("littlecms2", "LITTLECMS2"), 

288 ("webp", "WEBP"), 

289 ("jpg", "JPEG"), 

290 ("jpg_2000", "OPENJPEG (JPEG2000)"), 

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

292 ("libtiff", "LIBTIFF"), 

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

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

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

296 ]: 

297 if check(name): 

298 v: str | None = None 

299 if name == "jpg": 

300 libjpeg_turbo_version = version_feature("libjpeg_turbo") 

301 if libjpeg_turbo_version is not None: 

302 v = "libjpeg-turbo " + libjpeg_turbo_version 

303 if v is None: 

304 v = version(name) 

305 if v is not None: 

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

307 if name == "littlecms2": 

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

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

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

311 if name == "raqm": 

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

313 v2 = version_feature(f) 

314 if v2 is not None: 

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

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

317 else: 

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

319 else: 

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

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

322 

323 if supported_formats: 

324 extensions = collections.defaultdict(list) 

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

326 extensions[i].append(ext) 

327 

328 for i in sorted(Image.ID): 

329 line = f"{i}" 

330 if i in Image.MIME: 

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

332 print(line, file=out) 

333 

334 if i in extensions: 

335 print( 

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

337 ) 

338 

339 features = [] 

340 if i in Image.OPEN: 

341 features.append("open") 

342 if i in Image.SAVE: 

343 features.append("save") 

344 if i in Image.SAVE_ALL: 

345 features.append("save_all") 

346 if i in Image.DECODERS: 

347 features.append("decode") 

348 if i in Image.ENCODERS: 

349 features.append("encode") 

350 

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

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