Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1import contextlib 

2import functools 

3import inspect 

4import warnings 

5 

6 

7class MatplotlibDeprecationWarning(UserWarning): 

8 """ 

9 A class for issuing deprecation warnings for Matplotlib users. 

10 

11 In light of the fact that Python builtin DeprecationWarnings are ignored 

12 by default as of Python 2.7 (see link below), this class was put in to 

13 allow for the signaling of deprecation, but via UserWarnings which are not 

14 ignored by default. 

15 

16 https://docs.python.org/dev/whatsnew/2.7.html#the-future-for-python-2-x 

17 """ 

18 

19 

20mplDeprecation = MatplotlibDeprecationWarning 

21"""mplDeprecation is deprecated. Use MatplotlibDeprecationWarning instead.""" 

22 

23 

24def _generate_deprecation_warning( 

25 since, message='', name='', alternative='', pending=False, obj_type='', 

26 addendum='', *, removal=''): 

27 if pending: 

28 if removal: 

29 raise ValueError( 

30 "A pending deprecation cannot have a scheduled removal") 

31 else: 

32 if removal: 

33 removal = "in {}".format(removal) 

34 else: 

35 removal = {"2.2": "in 3.1", "3.0": "in 3.2", "3.1": "in 3.3"}.get( 

36 since, "two minor releases later") 

37 if not message: 

38 message = ( 

39 "\nThe %(name)s %(obj_type)s" 

40 + (" will be deprecated in a future version" 

41 if pending else 

42 (" was deprecated in Matplotlib %(since)s" 

43 + (" and will be removed %(removal)s" 

44 if removal else 

45 ""))) 

46 + "." 

47 + (" Use %(alternative)s instead." if alternative else "") 

48 + (" %(addendum)s" if addendum else "")) 

49 warning_cls = (PendingDeprecationWarning if pending 

50 else MatplotlibDeprecationWarning) 

51 return warning_cls(message % dict( 

52 func=name, name=name, obj_type=obj_type, since=since, removal=removal, 

53 alternative=alternative, addendum=addendum)) 

54 

55 

56def warn_deprecated( 

57 since, *, message='', name='', alternative='', pending=False, 

58 obj_type='', addendum='', removal=''): 

59 """ 

60 Used to display deprecation in a standard way. 

61 

62 Parameters 

63 ---------- 

64 since : str 

65 The release at which this API became deprecated. 

66 

67 message : str, optional 

68 Override the default deprecation message. The format 

69 specifier `%(name)s` may be used for the name of the function, 

70 and `%(alternative)s` may be used in the deprecation message 

71 to insert the name of an alternative to the deprecated 

72 function. `%(obj_type)s` may be used to insert a friendly name 

73 for the type of object being deprecated. 

74 

75 name : str, optional 

76 The name of the deprecated object. 

77 

78 alternative : str, optional 

79 An alternative API that the user may use in place of the deprecated 

80 API. The deprecation warning will tell the user about this alternative 

81 if provided. 

82 

83 pending : bool, optional 

84 If True, uses a PendingDeprecationWarning instead of a 

85 DeprecationWarning. Cannot be used together with *removal*. 

86 

87 obj_type : str, optional 

88 The object type being deprecated. 

89 

90 addendum : str, optional 

91 Additional text appended directly to the final message. 

92 

93 removal : str, optional 

94 The expected removal version. With the default (an empty string), a 

95 removal version is automatically computed from *since*. Set to other 

96 Falsy values to not schedule a removal date. Cannot be used together 

97 with *pending*. 

98 

99 Examples 

100 -------- 

101 Basic example:: 

102 

103 # To warn of the deprecation of "matplotlib.name_of_module" 

104 warn_deprecated('1.4.0', name='matplotlib.name_of_module', 

105 obj_type='module') 

106 """ 

107 warning = _generate_deprecation_warning( 

108 since, message, name, alternative, pending, obj_type, addendum, 

109 removal=removal) 

110 from . import _warn_external 

111 _warn_external(warning) 

112 

113 

114def deprecated(since, *, message='', name='', alternative='', pending=False, 

115 obj_type=None, addendum='', removal=''): 

116 """ 

117 Decorator to mark a function, a class, or a property as deprecated. 

118 

119 When deprecating a classmethod, a staticmethod, or a property, the 

120 ``@deprecated`` decorator should go *under* the ``@classmethod``, etc. 

121 decorator (i.e., `deprecated` should directly decorate the underlying 

122 callable). 

123 

124 Parameters 

125 ---------- 

126 since : str 

127 The release at which this API became deprecated. This is 

128 required. 

129 

130 message : str, optional 

131 Override the default deprecation message. The format 

132 specifier `%(name)s` may be used for the name of the object, 

133 and `%(alternative)s` may be used in the deprecation message 

134 to insert the name of an alternative to the deprecated 

135 object. 

136 

137 name : str, optional 

138 The name used in the deprecation message; if not provided, the name 

139 is automatically determined from the deprecated object. 

140 

141 alternative : str, optional 

142 An alternative API that the user may use in place of the deprecated 

143 API. The deprecation warning will tell the user about this alternative 

144 if provided. 

145 

146 pending : bool, optional 

147 If True, uses a PendingDeprecationWarning instead of a 

148 DeprecationWarning. Cannot be used together with *removal*. 

149 

150 obj_type : str, optional 

151 The object type being deprecated; by default, 'class' if decorating 

152 a class, 'attribute' if decorating a property, 'function' otherwise. 

153 

154 addendum : str, optional 

155 Additional text appended directly to the final message. 

156 

157 removal : str, optional 

158 The expected removal version. With the default (an empty string), a 

159 removal version is automatically computed from *since*. Set to other 

160 Falsy values to not schedule a removal date. Cannot be used together 

161 with *pending*. 

162 

163 Examples 

164 -------- 

165 Basic example:: 

166 

167 @deprecated('1.4.0') 

168 def the_function_to_deprecate(): 

169 pass 

170 """ 

171 

172 def deprecate(obj, message=message, name=name, alternative=alternative, 

173 pending=pending, obj_type=obj_type, addendum=addendum): 

174 

175 if isinstance(obj, type): 

176 if obj_type is None: 

177 obj_type = "class" 

178 func = obj.__init__ 

179 name = name or obj.__name__ 

180 old_doc = obj.__doc__ 

181 

182 def finalize(wrapper, new_doc): 

183 try: 

184 obj.__doc__ = new_doc 

185 except AttributeError: # Can't set on some extension objects. 

186 pass 

187 obj.__init__ = functools.wraps(obj.__init__)(wrapper) 

188 return obj 

189 

190 elif isinstance(obj, property): 

191 obj_type = "attribute" 

192 func = None 

193 name = name or obj.fget.__name__ 

194 old_doc = obj.__doc__ 

195 

196 class _deprecated_property(property): 

197 def __get__(self, instance, owner): 

198 if instance is not None: 

199 from . import _warn_external 

200 _warn_external(warning) 

201 return super().__get__(instance, owner) 

202 

203 def __set__(self, instance, value): 

204 if instance is not None: 

205 from . import _warn_external 

206 _warn_external(warning) 

207 return super().__set__(instance, value) 

208 

209 def __delete__(self, instance): 

210 if instance is not None: 

211 from . import _warn_external 

212 _warn_external(warning) 

213 return super().__delete__(instance) 

214 

215 def finalize(_, new_doc): 

216 return _deprecated_property( 

217 fget=obj.fget, fset=obj.fset, fdel=obj.fdel, doc=new_doc) 

218 

219 else: 

220 if obj_type is None: 

221 obj_type = "function" 

222 func = obj 

223 name = name or obj.__name__ 

224 old_doc = func.__doc__ 

225 

226 def finalize(wrapper, new_doc): 

227 wrapper = functools.wraps(func)(wrapper) 

228 wrapper.__doc__ = new_doc 

229 return wrapper 

230 

231 warning = _generate_deprecation_warning( 

232 since, message, name, alternative, pending, obj_type, addendum, 

233 removal=removal) 

234 

235 def wrapper(*args, **kwargs): 

236 from . import _warn_external 

237 _warn_external(warning) 

238 return func(*args, **kwargs) 

239 

240 old_doc = inspect.cleandoc(old_doc or '').strip('\n') 

241 

242 notes_header = '\nNotes\n-----' 

243 new_doc = (f"[*Deprecated*] {old_doc}\n" 

244 f"{notes_header if notes_header not in old_doc else ''}\n" 

245 f".. deprecated:: {since}\n" 

246 f" {message.strip()}") 

247 

248 if not old_doc: 

249 # This is to prevent a spurious 'unexpected unindent' warning from 

250 # docutils when the original docstring was blank. 

251 new_doc += r'\ ' 

252 

253 return finalize(wrapper, new_doc) 

254 

255 return deprecate 

256 

257 

258def _rename_parameter(since, old, new, func=None): 

259 """ 

260 Decorator indicating that parameter *old* of *func* is renamed to *new*. 

261 

262 The actual implementation of *func* should use *new*, not *old*. If *old* 

263 is passed to *func*, a DeprecationWarning is emitted, and its value is 

264 used, even if *new* is also passed by keyword (this is to simplify pyplot 

265 wrapper functions, which always pass *new* explicitly to the Axes method). 

266 If *new* is also passed but positionally, a TypeError will be raised by the 

267 underlying function during argument binding. 

268 

269 Examples 

270 -------- 

271 :: 

272 

273 @_rename_parameter("3.1", "bad_name", "good_name") 

274 def func(good_name): ... 

275 """ 

276 

277 if func is None: 

278 return functools.partial(_rename_parameter, since, old, new) 

279 

280 signature = inspect.signature(func) 

281 assert old not in signature.parameters, ( 

282 f"Matplotlib internal error: {old!r} cannot be a parameter for " 

283 f"{func.__name__}()") 

284 assert new in signature.parameters, ( 

285 f"Matplotlib internal error: {new!r} must be a parameter for " 

286 f"{func.__name__}()") 

287 

288 @functools.wraps(func) 

289 def wrapper(*args, **kwargs): 

290 if old in kwargs: 

291 warn_deprecated( 

292 since, message=f"The {old!r} parameter of {func.__name__}() " 

293 f"has been renamed {new!r} since Matplotlib {since}; support " 

294 f"for the old name will be dropped %(removal)s.") 

295 kwargs[new] = kwargs.pop(old) 

296 return func(*args, **kwargs) 

297 

298 # wrapper() must keep the same documented signature as func(): if we 

299 # instead made both *old* and *new* appear in wrapper()'s signature, they 

300 # would both show up in the pyplot function for an Axes method as well and 

301 # pyplot would explicitly pass both arguments to the Axes method. 

302 

303 return wrapper 

304 

305 

306class _deprecated_parameter_class: 

307 def __repr__(self): 

308 return "<deprecated parameter>" 

309 

310 

311_deprecated_parameter = _deprecated_parameter_class() 

312 

313 

314def _delete_parameter(since, name, func=None): 

315 """ 

316 Decorator indicating that parameter *name* of *func* is being deprecated. 

317 

318 The actual implementation of *func* should keep the *name* parameter in its 

319 signature. 

320 

321 Parameters that come after the deprecated parameter effectively become 

322 keyword-only (as they cannot be passed positionally without triggering the 

323 DeprecationWarning on the deprecated parameter), and should be marked as 

324 such after the deprecation period has passed and the deprecated parameter 

325 is removed. 

326 

327 Examples 

328 -------- 

329 :: 

330 

331 @_delete_parameter("3.1", "unused") 

332 def func(used_arg, other_arg, unused, more_args): ... 

333 """ 

334 

335 if func is None: 

336 return functools.partial(_delete_parameter, since, name) 

337 

338 signature = inspect.signature(func) 

339 assert name in signature.parameters, ( 

340 f"Matplotlib internal error: {name!r} must be a parameter for " 

341 f"{func.__name__}()") 

342 func.__signature__ = signature.replace(parameters=[ 

343 param.replace(default=_deprecated_parameter) if param.name == name 

344 else param 

345 for param in signature.parameters.values()]) 

346 

347 @functools.wraps(func) 

348 def wrapper(*args, **kwargs): 

349 arguments = func.__signature__.bind(*args, **kwargs).arguments 

350 # We cannot just check `name not in arguments` because the pyplot 

351 # wrappers always pass all arguments explicitly. 

352 if name in arguments and arguments[name] != _deprecated_parameter: 

353 warn_deprecated( 

354 since, message=f"The {name!r} parameter of {func.__name__}() " 

355 f"is deprecated since Matplotlib {since} and will be removed " 

356 f"%(removal)s. If any parameter follows {name!r}, they " 

357 f"should be pass as keyword, not positionally.") 

358 return func(*args, **kwargs) 

359 

360 return wrapper 

361 

362 

363def _make_keyword_only(since, name, func=None): 

364 """ 

365 Decorator indicating that passing parameter *name* (or any of the following 

366 ones) positionally to *func* is being deprecated. 

367 

368 Note that this decorator **cannot** be applied to a function that has a 

369 pyplot-level wrapper, as the wrapper always pass all arguments by keyword. 

370 If it is used, users will see spurious DeprecationWarnings every time they 

371 call the pyplot wrapper. 

372 """ 

373 

374 if func is None: 

375 return functools.partial(_make_keyword_only, since, name) 

376 

377 signature = inspect.signature(func) 

378 POK = inspect.Parameter.POSITIONAL_OR_KEYWORD 

379 KWO = inspect.Parameter.KEYWORD_ONLY 

380 assert (name in signature.parameters 

381 and signature.parameters[name].kind == POK), ( 

382 f"Matplotlib internal error: {name!r} must be a positional-or-keyword " 

383 f"parameter for {func.__name__}()") 

384 names = [*signature.parameters] 

385 kwonly = [name for name in names[names.index(name):] 

386 if signature.parameters[name].kind == POK] 

387 func.__signature__ = signature.replace(parameters=[ 

388 param.replace(kind=KWO) if param.name in kwonly else param 

389 for param in signature.parameters.values()]) 

390 

391 @functools.wraps(func) 

392 def wrapper(*args, **kwargs): 

393 bound = signature.bind(*args, **kwargs) 

394 if name in bound.arguments and name not in kwargs: 

395 warn_deprecated( 

396 since, message="Passing the %(name)s %(obj_type)s " 

397 "positionally is deprecated since Matplotlib %(since)s; the " 

398 "parameter will become keyword-only %(removal)s.", 

399 name=name, obj_type=f"parameter of {func.__name__}()") 

400 return func(*args, **kwargs) 

401 

402 return wrapper 

403 

404 

405@contextlib.contextmanager 

406def _suppress_matplotlib_deprecation_warning(): 

407 with warnings.catch_warnings(): 

408 warnings.simplefilter("ignore", MatplotlibDeprecationWarning) 

409 yield