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

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

165 statements  

1from __future__ import annotations 

2 

3__lazy_modules__ = {"collections", "warnings"} 

4 

5import collections 

6import os 

7import sys 

8import warnings 

9 

10import PIL 

11 

12from . import Image 

13 

14TYPE_CHECKING = False 

15if TYPE_CHECKING: 

16 from typing import IO 

17modules = { 

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

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

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

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

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

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

24} 

25 

26 

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

28 """ 

29 Checks if a module is available. 

30 

31 :param feature: The module to check for. 

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

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

34 """ 

35 if feature not in modules: 

36 msg = f"Unknown module {feature}" 

37 raise ValueError(msg) 

38 

39 module, ver = modules[feature] 

40 

41 try: 

42 __import__(module) 

43 return True 

44 except ModuleNotFoundError: 

45 return False 

46 except ImportError as ex: 

47 warnings.warn(str(ex)) 

48 return False 

49 

50 

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

52 """ 

53 :param feature: The module to check for. 

54 :returns: 

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

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

57 """ 

58 if not check_module(feature): 

59 return None 

60 

61 module, ver = modules[feature] 

62 

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

64 

65 

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

67 """ 

68 :returns: A list of all supported modules. 

69 """ 

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

71 

72 

73codecs = { 

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

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

76 "zlib": ("zip", "zlib"), 

77 "libtiff": ("libtiff", "libtiff"), 

78} 

79 

80 

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

82 """ 

83 Checks if a codec is available. 

84 

85 :param feature: The codec to check for. 

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

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

88 """ 

89 if feature not in codecs: 

90 msg = f"Unknown codec {feature}" 

91 raise ValueError(msg) 

92 

93 codec, lib = codecs[feature] 

94 

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

96 

97 

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

99 """ 

100 :param feature: The codec to check for. 

101 :returns: 

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

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

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

105 """ 

106 if not check_codec(feature): 

107 return None 

108 

109 codec, lib = codecs[feature] 

110 

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

112 

113 if feature == "libtiff": 

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

115 

116 return version 

117 

118 

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

120 """ 

121 :returns: A list of all supported codecs. 

122 """ 

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

124 

125 

126features: dict[str, tuple[str, str, str | 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 try: 

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

154 return getattr(imported_module, flag) 

155 except ModuleNotFoundError: 

156 return None 

157 except ImportError as ex: 

158 warnings.warn(str(ex)) 

159 return None 

160 

161 

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

163 """ 

164 :param feature: The feature to check for. 

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

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

167 """ 

168 if not check_feature(feature): 

169 return None 

170 

171 module, flag, ver = features[feature] 

172 

173 if ver is None: 

174 return None 

175 

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

177 

178 

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

180 """ 

181 :returns: A list of all supported features. 

182 """ 

183 return [f for f in features if check_feature(f)] 

184 

185 

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

187 """ 

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

189 :returns: 

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

191 ``False`` or ``None`` otherwise. 

192 """ 

193 

194 if feature in modules: 

195 return check_module(feature) 

196 if feature in codecs: 

197 return check_codec(feature) 

198 if feature in features: 

199 return check_feature(feature) 

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

201 return False 

202 

203 

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

205 """ 

206 :param feature: 

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

208 :returns: 

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

210 """ 

211 if feature in modules: 

212 return version_module(feature) 

213 if feature in codecs: 

214 return version_codec(feature) 

215 if feature in features: 

216 return version_feature(feature) 

217 return None 

218 

219 

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

221 """ 

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

223 """ 

224 

225 ret = get_supported_modules() 

226 ret.extend(get_supported_features()) 

227 ret.extend(get_supported_codecs()) 

228 return ret 

229 

230 

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

232 """ 

233 Prints information about this installation of Pillow. 

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

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

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

237 image file formats. 

238 

239 :param out: 

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

241 :param supported_formats: 

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

243 """ 

244 

245 if out is None: 

246 out = sys.stdout 

247 

248 Image.init() 

249 

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

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

252 py_version_lines = sys.version.splitlines() 

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

254 for py_version in py_version_lines[1:]: 

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

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

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

258 if sys.prefix != sys.base_prefix: 

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

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

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

262 print( 

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

264 file=out, 

265 ) 

266 print( 

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

268 file=out, 

269 ) 

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

271 

272 for name, feature in [ 

273 ("pil", "PIL CORE"), 

274 ("tkinter", "TKINTER"), 

275 ("freetype2", "FREETYPE2"), 

276 ("littlecms2", "LITTLECMS2"), 

277 ("webp", "WEBP"), 

278 ("avif", "AVIF"), 

279 ("jpg", "JPEG"), 

280 ("jpg_2000", "OPENJPEG (JPEG2000)"), 

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

282 ("libtiff", "LIBTIFF"), 

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

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

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

286 ]: 

287 if check(name): 

288 v: str | None = None 

289 if name == "jpg": 

290 libjpeg_turbo_version = version_feature("libjpeg_turbo") 

291 if libjpeg_turbo_version is not None: 

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

293 v += " " + libjpeg_turbo_version 

294 if v is None: 

295 v = version(name) 

296 if v is not None: 

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

298 if name == "littlecms2": 

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

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

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

302 if name == "zlib": 

303 zlib_ng_version = version_feature("zlib_ng") 

304 if zlib_ng_version is not None: 

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

306 elif name == "raqm": 

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

308 v2 = version_feature(f) 

309 if v2 is not None: 

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

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

312 else: 

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

314 else: 

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

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

317 

318 if supported_formats: 

319 extensions = collections.defaultdict(list) 

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

321 extensions[i].append(ext) 

322 

323 for i in sorted(Image.ID): 

324 line = f"{i}" 

325 if i in Image.MIME: 

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

327 print(line, file=out) 

328 

329 if i in extensions: 

330 print( 

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

332 ) 

333 

334 features = [] 

335 if i in Image.OPEN: 

336 features.append("open") 

337 if i in Image.SAVE: 

338 features.append("save") 

339 if i in Image.SAVE_ALL: 

340 features.append("save_all") 

341 if i in Image.DECODERS: 

342 features.append("decode") 

343 if i in Image.ENCODERS: 

344 features.append("encode") 

345 

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

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