From 08ea277eceed8070ef263f8b4051f27e71c5ce03 Mon Sep 17 00:00:00 2001 From: Corenthin ZOZOR Date: Mon, 16 Jun 2025 16:33:39 +0200 Subject: [PATCH 1/7] Add xlim / ylim autogeneration - Create get_signature method - Create AXES_GETTER_SETTER_TEMPLATE - Create call_param method on generate_function (cherry picked from commit e57b32165cae851b3f9cb846e37e3e49339aed43) --- lib/matplotlib/pyplot.py | 66 +++++++++++++++++++++ tools/boilerplate.py | 120 +++++++++++++++++++++++++++------------ 2 files changed, 151 insertions(+), 35 deletions(-) diff --git a/lib/matplotlib/pyplot.py b/lib/matplotlib/pyplot.py index cf5c9b4b739f..e6bde60671d5 100644 --- a/lib/matplotlib/pyplot.py +++ b/lib/matplotlib/pyplot.py @@ -4457,6 +4457,72 @@ def yscale(value: str | ScaleBase, **kwargs) -> None: gca().set_yscale(value, **kwargs) +# Autogenerated by boilerplate.py. Do not edit as changes will be lost. +@overload +@_copy_docstring_and_deprecators(Axes.get_xlim) +def xlim() -> tuple[float, float]: + ... + + +# Autogenerated by boilerplate.py. Do not edit as changes will be lost. +@overload +@_copy_docstring_and_deprecators(Axes.set_xlim) +def xlim( + left: float | tuple[float, float] | None = None, + right: float | None = None, + *, + emit: bool = True, + auto: bool | None = False, + xmin: float | None = None, + xmax: float | None = None, +) -> tuple[float, float]: + ... + + +# Autogenerated by boilerplate.py. Do not edit as changes will be lost. +@_copy_docstring_and_deprecators(Axes.get_xlim) +def xlim(*args, **kwargs): + ax = gca() + if not args and not kwargs: + return ax.get_xlim() + + ret = ax.set_xlim(*args, **kwargs) + return ret + + +# Autogenerated by boilerplate.py. Do not edit as changes will be lost. +@overload +@_copy_docstring_and_deprecators(Axes.get_ylim) +def ylim() -> tuple[float, float]: + ... + + +# Autogenerated by boilerplate.py. Do not edit as changes will be lost. +@overload +@_copy_docstring_and_deprecators(Axes.set_ylim) +def ylim( + bottom: float | tuple[float, float] | None = None, + top: float | None = None, + *, + emit: bool = True, + auto: bool | None = False, + ymin: float | None = None, + ymax: float | None = None, +) -> tuple[float, float]: + ... + + +# Autogenerated by boilerplate.py. Do not edit as changes will be lost. +@_copy_docstring_and_deprecators(Axes.get_ylim) +def ylim(*args, **kwargs): + ax = gca() + if not args and not kwargs: + return ax.get_ylim() + + ret = ax.set_ylim(*args, **kwargs) + return ret + + # Autogenerated by boilerplate.py. Do not edit as changes will be lost. def autumn() -> None: """ diff --git a/tools/boilerplate.py b/tools/boilerplate.py index 11ec15ac1c44..72e4100f0f16 100644 --- a/tools/boilerplate.py +++ b/tools/boilerplate.py @@ -54,6 +54,25 @@ def {name}{signature}: {return_statement}gca().{called_name}{call} """ +AXES_GETTER_SETTER_TEMPLATE = AUTOGEN_MSG + """ +@overload +@_copy_docstring_and_deprecators(Axes.get_{called_name}) +def {name}() -> {get_return_type}: ... +""" + AUTOGEN_MSG + """ +@overload +@_copy_docstring_and_deprecators(Axes.set_{called_name}) +def {name}{signature}: ... +""" + AUTOGEN_MSG + """ +@_copy_docstring_and_deprecators(Axes.get_{called_name}) +def {name}(*args, **kwargs): + ax = gca() + if not args and not kwargs: + return ax.get_{called_name}() + + ret = ax.set_{called_name}(*args, **kwargs) + return ret +""" + FIGURE_METHOD_TEMPLATE = AUTOGEN_MSG + """ @_copy_docstring_and_deprecators(Figure.{called_name}) def {name}{signature}: @@ -102,6 +121,7 @@ class direct_repr: """ A placeholder class to destringify annotations from ast """ + def __init__(self, value): self._repr = value @@ -109,7 +129,7 @@ def __repr__(self): return self._repr -def generate_function(name, called_fullname, template, **kwargs): +def generate_function(name, called_fullname, template, gettersetter=False, **kwargs): """ Create a wrapper function *pyplot_name* calling *call_name*. @@ -127,6 +147,11 @@ def generate_function(name, called_fullname, template, **kwargs): - signature: The function signature (including parentheses). - called_name: The name of the called function. - call: Parameters passed to *called_name* (including parentheses). + gettersetter : bool + Indicate if the method to be wrapped is correponding to a getter and setter. A new placeholdr is filled : + + - get_return_type: The type returned by the getter + - set_return_type: The type returned by the setter **kwargs Additional parameters are passed to ``template.format()``. @@ -135,15 +160,14 @@ def generate_function(name, called_fullname, template, **kwargs): class_name, called_name = called_fullname.split('.') class_ = {'Axes': Axes, 'Figure': Figure}[class_name] - meth = getattr(class_, called_name) - decorator = _api.deprecation.DECORATORS.get(meth) - # Generate the wrapper with the non-kwonly signature, as it will get - # redecorated with make_keyword_only by _copy_docstring_and_deprecators. - if decorator and decorator.func is _api.make_keyword_only: - meth = meth.__wrapped__ + if not gettersetter: + signature = get_signature(class_, called_name) + else: + getter_signature = get_signature(class_, f"get_{called_name}") + kwargs.setdefault("get_return_type", str(getter_signature.return_annotation)) - annotated_trees = get_ast_mro_trees(class_) - signature = get_matching_signature(meth, annotated_trees) + signature = get_signature(class_, f"set_{called_name}") + kwargs.setdefault('return_type', str(signature.return_annotation)) # Replace self argument. params = list(signature.parameters.values())[1:] @@ -152,30 +176,30 @@ def generate_function(name, called_fullname, template, **kwargs): param.replace(default=value_formatter(param.default)) if param.default is not param.empty else param for param in params])) + # How to call the wrapped function. - call = '(' + ', '.join(( - # Pass "intended-as-positional" parameters positionally to avoid - # forcing third-party subclasses to reproduce the parameter names. - '{0}' - if param.kind in [ - Parameter.POSITIONAL_OR_KEYWORD] - and param.default is Parameter.empty else - # Only pass the data kwarg if it is actually set, to avoid forcing - # third-party subclasses to support it. - '**({{"data": data}} if data is not None else {{}})' - if param.name == "data" else - '{0}={0}' - if param.kind in [ - Parameter.POSITIONAL_OR_KEYWORD, - Parameter.KEYWORD_ONLY] else - '{0}' - if param.kind is Parameter.POSITIONAL_ONLY else - '*{0}' - if param.kind is Parameter.VAR_POSITIONAL else - '**{0}' - if param.kind is Parameter.VAR_KEYWORD else - None).format(param.name) - for param in params) + ')' + + def call_param(param: Parameter): + match param.kind: + # Pass "intended-as-positional" parameters positionally to avoid + # forcing third-party subclasses to reproduce the parameter names. + case Parameter.POSITIONAL_OR_KEYWORD if param.default is Parameter.empty: + return '{0}' + # Only pass the data kwarg if it is actually set, to avoid forcing + # third-party subclasses to support it. + case _ if param.name == "data": + return '**({{"data": data}} if data is not None else {{}})' + case Parameter.POSITIONAL_OR_KEYWORD | Parameter.KEYWORD_ONLY: + return '{0}={0}' + case Parameter.POSITIONAL_ONLY: + return '{0}' + case Parameter.VAR_POSITIONAL: + return '*{0}' + case Parameter.VAR_KEYWORD: + return '**{0}' + return None + + call = '(' + ', '.join((call_param(param)).format(param.name) for param in params) + ')' return_statement = 'return ' if has_return_value else '' # Bail out in case of name collision. for reserved in ('gca', 'gci', 'gcf', '__ret'): @@ -286,7 +310,12 @@ def boilerplate_gen(): 'xlabel:set_xlabel', 'ylabel:set_ylabel', 'xscale:set_xscale', - 'yscale:set_yscale', + 'yscale:set_yscale' + ) + + _axes_getter_setters = ( + 'xlim', + 'ylim', ) cmappable = { @@ -341,6 +370,14 @@ def boilerplate_gen(): yield generate_function(name, f'Axes.{called_name}', template, sci_command=cmappable.get(name)) + for spec in _axes_getter_setters: + if ':' in spec: + name, called_name = spec.split(':') + else: + name = called_name = spec + yield generate_function(name, f'Axes.{called_name}', + AXES_GETTER_SETTER_TEMPLATE, True) + cmaps = ( 'autumn', 'bone', @@ -405,6 +442,19 @@ def get_ast_mro_trees(cls): return [get_ast_tree(c) for c in cls.__mro__ if c.__module__ != "builtins"] +def get_signature(class_, name): + meth = getattr(class_, name) + + decorator = _api.deprecation.DECORATORS.get(meth) + # Generate the wrapper with the non-kwonly signature, as it will get + # redecorated with make_keyword_only by _copy_docstring_and_deprecators. + if decorator and decorator.func is _api.make_keyword_only: + meth = meth.__wrapped__ + + annotated_trees = get_ast_mro_trees(class_) + return get_matching_signature(meth, annotated_trees) + + def get_matching_signature(method, trees): sig = inspect.signature(method) for tree in trees: @@ -460,10 +510,10 @@ def update_sig_from_node(node, sig): if len(sys.argv) > 1: pyplot_path = Path(sys.argv[1]) else: - mpl_path = (Path(__file__).parent / ".." /"lib"/"matplotlib").resolve() + mpl_path = (Path(__file__).parent / ".." / "lib" / "matplotlib").resolve() pyplot_path = mpl_path / "pyplot.py" for cls in [Axes, Figure]: - if mpl_path not in Path(inspect.getfile(cls)).parents: + if mpl_path not in Path(inspect.getfile(cls)).parents: raise RuntimeError( f"{cls.__name__} import path is not {mpl_path}.\n" "Please make sure your Matplotlib installation " From f78a4a5f46532341dd8cba6f5fc5ea75cc27d0a4 Mon Sep 17 00:00:00 2001 From: Corenthin ZOZOR Date: Fri, 20 Jun 2025 21:12:24 +0200 Subject: [PATCH 2/7] Format with ruff (cherry picked from commit 64e7921b0b3f56c88c1f449a4f2081e862289279) --- tools/boilerplate.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/tools/boilerplate.py b/tools/boilerplate.py index 72e4100f0f16..778729abe12d 100644 --- a/tools/boilerplate.py +++ b/tools/boilerplate.py @@ -129,7 +129,13 @@ def __repr__(self): return self._repr -def generate_function(name, called_fullname, template, gettersetter=False, **kwargs): +def generate_function( + name, + called_fullname, + template, + gettersetter=False, + **kwargs +): """ Create a wrapper function *pyplot_name* calling *call_name*. @@ -199,7 +205,9 @@ def call_param(param: Parameter): return '**{0}' return None - call = '(' + ', '.join((call_param(param)).format(param.name) for param in params) + ')' + call = '(' + ', '.join( + (call_param(param)).format(param.name) for param in params + ) + ')' return_statement = 'return ' if has_return_value else '' # Bail out in case of name collision. for reserved in ('gca', 'gci', 'gcf', '__ret'): From 8e41f892af7aadd6e1c961cfc51d77c7845e809e Mon Sep 17 00:00:00 2001 From: Corenthin ZOZOR Date: Fri, 20 Jun 2025 20:24:18 +0200 Subject: [PATCH 3/7] Remove old xlim and ylim (cherry picked from commit 66ee0714ff310e0693e05c4616bbb702e45a6407) --- lib/matplotlib/pyplot.py | 75 ---------------------------------------- 1 file changed, 75 deletions(-) diff --git a/lib/matplotlib/pyplot.py b/lib/matplotlib/pyplot.py index e6bde60671d5..0855f0f97fdf 100644 --- a/lib/matplotlib/pyplot.py +++ b/lib/matplotlib/pyplot.py @@ -2095,81 +2095,6 @@ def box(on: bool | None = None) -> None: ## Axis ## - -def xlim(*args, **kwargs) -> tuple[float, float]: - """ - Get or set the x limits of the current Axes. - - Call signatures:: - - left, right = xlim() # return the current xlim - xlim((left, right)) # set the xlim to left, right - xlim(left, right) # set the xlim to left, right - - If you do not specify args, you can pass *left* or *right* as kwargs, - i.e.:: - - xlim(right=3) # adjust the right leaving left unchanged - xlim(left=1) # adjust the left leaving right unchanged - - Setting limits turns autoscaling off for the x-axis. - - Returns - ------- - left, right - A tuple of the new x-axis limits. - - Notes - ----- - Calling this function with no arguments (e.g. ``xlim()``) is the pyplot - equivalent of calling `~.Axes.get_xlim` on the current Axes. - Calling this function with arguments is the pyplot equivalent of calling - `~.Axes.set_xlim` on the current Axes. All arguments are passed though. - """ - ax = gca() - if not args and not kwargs: - return ax.get_xlim() - ret = ax.set_xlim(*args, **kwargs) - return ret - - -def ylim(*args, **kwargs) -> tuple[float, float]: - """ - Get or set the y-limits of the current Axes. - - Call signatures:: - - bottom, top = ylim() # return the current ylim - ylim((bottom, top)) # set the ylim to bottom, top - ylim(bottom, top) # set the ylim to bottom, top - - If you do not specify args, you can alternatively pass *bottom* or - *top* as kwargs, i.e.:: - - ylim(top=3) # adjust the top leaving bottom unchanged - ylim(bottom=1) # adjust the bottom leaving top unchanged - - Setting limits turns autoscaling off for the y-axis. - - Returns - ------- - bottom, top - A tuple of the new y-axis limits. - - Notes - ----- - Calling this function with no arguments (e.g. ``ylim()``) is the pyplot - equivalent of calling `~.Axes.get_ylim` on the current Axes. - Calling this function with arguments is the pyplot equivalent of calling - `~.Axes.set_ylim` on the current Axes. All arguments are passed though. - """ - ax = gca() - if not args and not kwargs: - return ax.get_ylim() - ret = ax.set_ylim(*args, **kwargs) - return ret - - def xticks( ticks: ArrayLike | None = None, labels: Sequence[str] | None = None, From 880e2628152a10014ddf688df9d05a9a601bf1c8 Mon Sep 17 00:00:00 2001 From: Corenthin ZOZOR Date: Sat, 21 Jun 2025 13:09:29 +0200 Subject: [PATCH 4/7] Format with ruff --- tools/boilerplate.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tools/boilerplate.py b/tools/boilerplate.py index 778729abe12d..4bbc75395a14 100644 --- a/tools/boilerplate.py +++ b/tools/boilerplate.py @@ -130,11 +130,11 @@ def __repr__(self): def generate_function( - name, - called_fullname, - template, - gettersetter=False, - **kwargs + name, + called_fullname, + template, + gettersetter=False, + **kwargs ): """ Create a wrapper function *pyplot_name* calling *call_name*. From 3f3743fcabf1411e27bfaf65178bc49bcb051ea5 Mon Sep 17 00:00:00 2001 From: Corenthin ZOZOR Date: Sat, 21 Jun 2025 13:14:59 +0200 Subject: [PATCH 5/7] Revert superfluous changes --- tools/boilerplate.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/boilerplate.py b/tools/boilerplate.py index 4bbc75395a14..6d08c6b9df4c 100644 --- a/tools/boilerplate.py +++ b/tools/boilerplate.py @@ -518,10 +518,10 @@ def update_sig_from_node(node, sig): if len(sys.argv) > 1: pyplot_path = Path(sys.argv[1]) else: - mpl_path = (Path(__file__).parent / ".." / "lib" / "matplotlib").resolve() + mpl_path = (Path(__file__).parent / ".." /"lib"/"matplotlib").resolve() pyplot_path = mpl_path / "pyplot.py" for cls in [Axes, Figure]: - if mpl_path not in Path(inspect.getfile(cls)).parents: + if mpl_path not in Path(inspect.getfile(cls)).parents: raise RuntimeError( f"{cls.__name__} import path is not {mpl_path}.\n" "Please make sure your Matplotlib installation " From b5dfb32058ef8ef10f931f0f337e9bffb2113338 Mon Sep 17 00:00:00 2001 From: ZPyrolink <38cz74@gmail.com> Date: Tue, 24 Jun 2025 20:29:43 +0200 Subject: [PATCH 6/7] Revert boilerplate.py and manually copy xlim and ylim overloads on pyplot.py --- lib/matplotlib/pyplot.py | 178 ++++++++++++++++++++++++--------------- tools/boilerplate.py | 124 ++++++++------------------- 2 files changed, 145 insertions(+), 157 deletions(-) diff --git a/lib/matplotlib/pyplot.py b/lib/matplotlib/pyplot.py index 0855f0f97fdf..c16b79895759 100644 --- a/lib/matplotlib/pyplot.py +++ b/lib/matplotlib/pyplot.py @@ -2095,6 +2095,118 @@ def box(on: bool | None = None) -> None: ## Axis ## + +@overload +def xlim() -> tuple[float, float]: + ... + + +# Autogenerated by boilerplate.py. Do not edit as changes will be lost. +@overload +def xlim( + left: float | tuple[float, float] | None = None, + right: float | None = None, + *, + emit: bool = True, + auto: bool | None = False, + xmin: float | None = None, + xmax: float | None = None, +) -> tuple[float, float]: + ... + + +def xlim(*args, **kwargs) -> tuple[float, float]: + """ + Get or set the x limits of the current Axes. + + Call signatures:: + + left, right = xlim() # return the current xlim + xlim((left, right)) # set the xlim to left, right + xlim(left, right) # set the xlim to left, right + + If you do not specify args, you can pass *left* or *right* as kwargs, + i.e.:: + + xlim(right=3) # adjust the right leaving left unchanged + xlim(left=1) # adjust the left leaving right unchanged + + Setting limits turns autoscaling off for the x-axis. + + Returns + ------- + left, right + A tuple of the new x-axis limits. + + Notes + ----- + Calling this function with no arguments (e.g. ``xlim()``) is the pyplot + equivalent of calling `~.Axes.get_xlim` on the current Axes. + Calling this function with arguments is the pyplot equivalent of calling + `~.Axes.set_xlim` on the current Axes. All arguments are passed though. + """ + ax = gca() + if not args and not kwargs: + return ax.get_xlim() + ret = ax.set_xlim(*args, **kwargs) + return ret + + +@overload +def ylim() -> tuple[float, float]: + ... + + +@overload +def ylim( + bottom: float | tuple[float, float] | None = None, + top: float | None = None, + *, + emit: bool = True, + auto: bool | None = False, + ymin: float | None = None, + ymax: float | None = None, +) -> tuple[float, float]: + ... + + +def ylim(*args, **kwargs) -> tuple[float, float]: + """ + Get or set the y-limits of the current Axes. + + Call signatures:: + + bottom, top = ylim() # return the current ylim + ylim((bottom, top)) # set the ylim to bottom, top + ylim(bottom, top) # set the ylim to bottom, top + + If you do not specify args, you can alternatively pass *bottom* or + *top* as kwargs, i.e.:: + + ylim(top=3) # adjust the top leaving bottom unchanged + ylim(bottom=1) # adjust the bottom leaving top unchanged + + Setting limits turns autoscaling off for the y-axis. + + Returns + ------- + bottom, top + A tuple of the new y-axis limits. + + Notes + ----- + Calling this function with no arguments (e.g. ``ylim()``) is the pyplot + equivalent of calling `~.Axes.get_ylim` on the current Axes. + Calling this function with arguments is the pyplot equivalent of calling + `~.Axes.set_ylim` on the current Axes. All arguments are passed though. + """ + ax = gca() + if not args and not kwargs: + return ax.get_ylim() + ret = ax.set_ylim(*args, **kwargs) + return ret + + def xticks( ticks: ArrayLike | None = None, labels: Sequence[str] | None = None, @@ -4382,72 +4494,6 @@ def yscale(value: str | ScaleBase, **kwargs) -> None: gca().set_yscale(value, **kwargs) -# Autogenerated by boilerplate.py. Do not edit as changes will be lost. -@overload -@_copy_docstring_and_deprecators(Axes.get_xlim) -def xlim() -> tuple[float, float]: - ... - - -# Autogenerated by boilerplate.py. Do not edit as changes will be lost. -@overload -@_copy_docstring_and_deprecators(Axes.set_xlim) -def xlim( - left: float | tuple[float, float] | None = None, - right: float | None = None, - *, - emit: bool = True, - auto: bool | None = False, - xmin: float | None = None, - xmax: float | None = None, -) -> tuple[float, float]: - ... - - -# Autogenerated by boilerplate.py. Do not edit as changes will be lost. -@_copy_docstring_and_deprecators(Axes.get_xlim) -def xlim(*args, **kwargs): - ax = gca() - if not args and not kwargs: - return ax.get_xlim() - - ret = ax.set_xlim(*args, **kwargs) - return ret - - -# Autogenerated by boilerplate.py. Do not edit as changes will be lost. -@overload -@_copy_docstring_and_deprecators(Axes.get_ylim) -def ylim() -> tuple[float, float]: - ... - - -# Autogenerated by boilerplate.py. Do not edit as changes will be lost. -@overload -@_copy_docstring_and_deprecators(Axes.set_ylim) -def ylim( - bottom: float | tuple[float, float] | None = None, - top: float | None = None, - *, - emit: bool = True, - auto: bool | None = False, - ymin: float | None = None, - ymax: float | None = None, -) -> tuple[float, float]: - ... - - -# Autogenerated by boilerplate.py. Do not edit as changes will be lost. -@_copy_docstring_and_deprecators(Axes.get_ylim) -def ylim(*args, **kwargs): - ax = gca() - if not args and not kwargs: - return ax.get_ylim() - - ret = ax.set_ylim(*args, **kwargs) - return ret - - # Autogenerated by boilerplate.py. Do not edit as changes will be lost. def autumn() -> None: """ diff --git a/tools/boilerplate.py b/tools/boilerplate.py index 6d08c6b9df4c..11ec15ac1c44 100644 --- a/tools/boilerplate.py +++ b/tools/boilerplate.py @@ -54,25 +54,6 @@ def {name}{signature}: {return_statement}gca().{called_name}{call} """ -AXES_GETTER_SETTER_TEMPLATE = AUTOGEN_MSG + """ -@overload -@_copy_docstring_and_deprecators(Axes.get_{called_name}) -def {name}() -> {get_return_type}: ... -""" + AUTOGEN_MSG + """ -@overload -@_copy_docstring_and_deprecators(Axes.set_{called_name}) -def {name}{signature}: ... -""" + AUTOGEN_MSG + """ -@_copy_docstring_and_deprecators(Axes.get_{called_name}) -def {name}(*args, **kwargs): - ax = gca() - if not args and not kwargs: - return ax.get_{called_name}() - - ret = ax.set_{called_name}(*args, **kwargs) - return ret -""" - FIGURE_METHOD_TEMPLATE = AUTOGEN_MSG + """ @_copy_docstring_and_deprecators(Figure.{called_name}) def {name}{signature}: @@ -121,7 +102,6 @@ class direct_repr: """ A placeholder class to destringify annotations from ast """ - def __init__(self, value): self._repr = value @@ -129,13 +109,7 @@ def __repr__(self): return self._repr -def generate_function( - name, - called_fullname, - template, - gettersetter=False, - **kwargs -): +def generate_function(name, called_fullname, template, **kwargs): """ Create a wrapper function *pyplot_name* calling *call_name*. @@ -153,11 +127,6 @@ def generate_function( - signature: The function signature (including parentheses). - called_name: The name of the called function. - call: Parameters passed to *called_name* (including parentheses). - gettersetter : bool - Indicate if the method to be wrapped is correponding to a getter and setter. A new placeholdr is filled : - - - get_return_type: The type returned by the getter - - set_return_type: The type returned by the setter **kwargs Additional parameters are passed to ``template.format()``. @@ -166,14 +135,15 @@ def generate_function( class_name, called_name = called_fullname.split('.') class_ = {'Axes': Axes, 'Figure': Figure}[class_name] - if not gettersetter: - signature = get_signature(class_, called_name) - else: - getter_signature = get_signature(class_, f"get_{called_name}") - kwargs.setdefault("get_return_type", str(getter_signature.return_annotation)) + meth = getattr(class_, called_name) + decorator = _api.deprecation.DECORATORS.get(meth) + # Generate the wrapper with the non-kwonly signature, as it will get + # redecorated with make_keyword_only by _copy_docstring_and_deprecators. + if decorator and decorator.func is _api.make_keyword_only: + meth = meth.__wrapped__ - signature = get_signature(class_, f"set_{called_name}") - kwargs.setdefault('return_type', str(signature.return_annotation)) + annotated_trees = get_ast_mro_trees(class_) + signature = get_matching_signature(meth, annotated_trees) # Replace self argument. params = list(signature.parameters.values())[1:] @@ -182,32 +152,30 @@ def generate_function( param.replace(default=value_formatter(param.default)) if param.default is not param.empty else param for param in params])) - # How to call the wrapped function. - - def call_param(param: Parameter): - match param.kind: - # Pass "intended-as-positional" parameters positionally to avoid - # forcing third-party subclasses to reproduce the parameter names. - case Parameter.POSITIONAL_OR_KEYWORD if param.default is Parameter.empty: - return '{0}' - # Only pass the data kwarg if it is actually set, to avoid forcing - # third-party subclasses to support it. - case _ if param.name == "data": - return '**({{"data": data}} if data is not None else {{}})' - case Parameter.POSITIONAL_OR_KEYWORD | Parameter.KEYWORD_ONLY: - return '{0}={0}' - case Parameter.POSITIONAL_ONLY: - return '{0}' - case Parameter.VAR_POSITIONAL: - return '*{0}' - case Parameter.VAR_KEYWORD: - return '**{0}' - return None - - call = '(' + ', '.join( - (call_param(param)).format(param.name) for param in params - ) + ')' + call = '(' + ', '.join(( + # Pass "intended-as-positional" parameters positionally to avoid + # forcing third-party subclasses to reproduce the parameter names. + '{0}' + if param.kind in [ + Parameter.POSITIONAL_OR_KEYWORD] + and param.default is Parameter.empty else + # Only pass the data kwarg if it is actually set, to avoid forcing + # third-party subclasses to support it. + '**({{"data": data}} if data is not None else {{}})' + if param.name == "data" else + '{0}={0}' + if param.kind in [ + Parameter.POSITIONAL_OR_KEYWORD, + Parameter.KEYWORD_ONLY] else + '{0}' + if param.kind is Parameter.POSITIONAL_ONLY else + '*{0}' + if param.kind is Parameter.VAR_POSITIONAL else + '**{0}' + if param.kind is Parameter.VAR_KEYWORD else + None).format(param.name) + for param in params) + ')' return_statement = 'return ' if has_return_value else '' # Bail out in case of name collision. for reserved in ('gca', 'gci', 'gcf', '__ret'): @@ -318,12 +286,7 @@ def boilerplate_gen(): 'xlabel:set_xlabel', 'ylabel:set_ylabel', 'xscale:set_xscale', - 'yscale:set_yscale' - ) - - _axes_getter_setters = ( - 'xlim', - 'ylim', + 'yscale:set_yscale', ) cmappable = { @@ -378,14 +341,6 @@ def boilerplate_gen(): yield generate_function(name, f'Axes.{called_name}', template, sci_command=cmappable.get(name)) - for spec in _axes_getter_setters: - if ':' in spec: - name, called_name = spec.split(':') - else: - name = called_name = spec - yield generate_function(name, f'Axes.{called_name}', - AXES_GETTER_SETTER_TEMPLATE, True) - cmaps = ( 'autumn', 'bone', @@ -450,19 +405,6 @@ def get_ast_mro_trees(cls): return [get_ast_tree(c) for c in cls.__mro__ if c.__module__ != "builtins"] -def get_signature(class_, name): - meth = getattr(class_, name) - - decorator = _api.deprecation.DECORATORS.get(meth) - # Generate the wrapper with the non-kwonly signature, as it will get - # redecorated with make_keyword_only by _copy_docstring_and_deprecators. - if decorator and decorator.func is _api.make_keyword_only: - meth = meth.__wrapped__ - - annotated_trees = get_ast_mro_trees(class_) - return get_matching_signature(meth, annotated_trees) - - def get_matching_signature(method, trees): sig = inspect.signature(method) for tree in trees: From 2f615827ce8771c1a541c6c442d38689c5368364 Mon Sep 17 00:00:00 2001 From: ZPyrolink <38cz74@gmail.com> Date: Tue, 24 Jun 2025 20:54:37 +0200 Subject: [PATCH 7/7] Remove incorrect comment about autogenerated method --- lib/matplotlib/pyplot.py | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/matplotlib/pyplot.py b/lib/matplotlib/pyplot.py index c16b79895759..afc2be9a1221 100644 --- a/lib/matplotlib/pyplot.py +++ b/lib/matplotlib/pyplot.py @@ -2101,7 +2101,6 @@ def xlim() -> tuple[float, float]: ... -# Autogenerated by boilerplate.py. Do not edit as changes will be lost. @overload def xlim( left: float | tuple[float, float] | None = None, pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy