@@ -249,6 +249,14 @@ def __set_name__(self, owner, name):
249
249
name = name ))
250
250
251
251
252
+ # Used by _copy_docstring_and_deprecators to redecorate pyplot wrappers and
253
+ # boilerplate.py to retrieve original signatures. It may seem natural to store
254
+ # this information as an attribute on the wrapper, but if the wrapper gets
255
+ # itself functools.wraps()ed, then such attributes are silently propagated to
256
+ # the outer wrapper, which is not desired.
257
+ DECORATORS = {}
258
+
259
+
252
260
def rename_parameter (since , old , new , func = None ):
253
261
"""
254
262
Decorator indicating that parameter *old* of *func* is renamed to *new*.
@@ -268,8 +276,10 @@ def rename_parameter(since, old, new, func=None):
268
276
def func(good_name): ...
269
277
"""
270
278
279
+ decorator = functools .partial (rename_parameter , since , old , new )
280
+
271
281
if func is None :
272
- return functools . partial ( rename_parameter , since , old , new )
282
+ return decorator
273
283
274
284
signature = inspect .signature (func )
275
285
assert old not in signature .parameters , (
@@ -294,6 +304,7 @@ def wrapper(*args, **kwargs):
294
304
# would both show up in the pyplot function for an Axes method as well and
295
305
# pyplot would explicitly pass both arguments to the Axes method.
296
306
307
+ DECORATORS [wrapper ] = decorator
297
308
return wrapper
298
309
299
310
@@ -330,8 +341,10 @@ def delete_parameter(since, name, func=None, **kwargs):
330
341
def func(used_arg, other_arg, unused, more_args): ...
331
342
"""
332
343
344
+ decorator = functools .partial (delete_parameter , since , name , ** kwargs )
345
+
333
346
if func is None :
334
- return functools . partial ( delete_parameter , since , name , ** kwargs )
347
+ return decorator
335
348
336
349
signature = inspect .signature (func )
337
350
# Name of `**kwargs` parameter of the decorated function, typically
@@ -399,17 +412,24 @@ def wrapper(*inner_args, **inner_kwargs):
399
412
** kwargs )
400
413
return func (* inner_args , ** inner_kwargs )
401
414
415
+ DECORATORS [wrapper ] = decorator
402
416
return wrapper
403
417
404
418
405
419
def make_keyword_only (since , name , func = None ):
406
420
"""
407
421
Decorator indicating that passing parameter *name* (or any of the following
408
422
ones) positionally to *func* is being deprecated.
423
+
424
+ When used on a method that has a pyplot wrapper, this should be the
425
+ outermost decorator, so that :file:`boilerplate.py` can access the original
426
+ signature.
409
427
"""
410
428
429
+ decorator = functools .partial (make_keyword_only , since , name )
430
+
411
431
if func is None :
412
- return functools . partial ( make_keyword_only , since , name )
432
+ return decorator
413
433
414
434
signature = inspect .signature (func )
415
435
POK = inspect .Parameter .POSITIONAL_OR_KEYWORD
@@ -419,26 +439,28 @@ def make_keyword_only(since, name, func=None):
419
439
f"Matplotlib internal error: { name !r} must be a positional-or-keyword "
420
440
f"parameter for { func .__name__ } ()" )
421
441
names = [* signature .parameters ]
422
- kwonly = [name for name in names [names .index (name ):]
442
+ name_idx = names .index (name )
443
+ kwonly = [name for name in names [name_idx :]
423
444
if signature .parameters [name ].kind == POK ]
424
- func .__signature__ = signature .replace (parameters = [
425
- param .replace (kind = KWO ) if param .name in kwonly else param
426
- for param in signature .parameters .values ()])
427
445
428
446
@functools .wraps (func )
429
447
def wrapper (* args , ** kwargs ):
430
448
# Don't use signature.bind here, as it would fail when stacked with
431
449
# rename_parameter and an "old" argument name is passed in
432
450
# (signature.bind would fail, but the actual call would succeed).
433
- idx = [* func .__signature__ .parameters ].index (name )
434
- if len (args ) > idx :
451
+ if len (args ) > name_idx :
435
452
warn_deprecated (
436
453
since , message = "Passing the %(name)s %(obj_type)s "
437
454
"positionally is deprecated since Matplotlib %(since)s; the "
438
455
"parameter will become keyword-only %(removal)s." ,
439
456
name = name , obj_type = f"parameter of { func .__name__ } ()" )
440
457
return func (* args , ** kwargs )
441
458
459
+ # Don't modify *func*'s signature, as boilerplate.py needs it.
460
+ wrapper .__signature__ = signature .replace (parameters = [
461
+ param .replace (kind = KWO ) if param .name in kwonly else param
462
+ for param in signature .parameters .values ()])
463
+ DECORATORS [wrapper ] = decorator
442
464
return wrapper
443
465
444
466
0 commit comments