You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
[MNT]: deprecate "which" parameter of Axes.set_position? #31254
As a user, I was always confused by the which parameter of set_position and under what circumstance one might set the active position. The docstring indicates that the original and active positions are usually the same unless a fixed aspect ratio is set.
Looking through the code of apply_aspect, I think it always updates the active position. Since apply_aspect gets called within draw, get_tightbbox and get_position, it's not clear to me that the user can ever effectively set the active position directly, unless they made an axes subclass that replaced apply_aspect.
apply_aspect itself uses a private _set_position that was factored out of set_position at #9082. So maybe the existence of the which parameter in the public function is a hangover from before that and is no longer needed.
Proposed fix
Either deprecate the which parameter in the public method so it only sets the original position, or make it clear in the docstring that setting the active position will almost never be effective.
changed the title [-][MNT]: deprecate *which* parameter of `Axes.set_position`?[/-][+][MNT]: deprecate _which_ parameter of `Axes.set_position`?[/+]on Mar 7, 2026
changed the title [-][MNT]: deprecate _which_ parameter of `Axes.set_position`?[/-][+][MNT]: deprecate "which" parameter of `Axes.set_position`?[/+]on Mar 7, 2026
Deprecations always introduce API changes. That is their purpose.
I meant that clarifying the docstring could be a lower-impact first step, since it avoids starting a deprecation cycle immediately. If the parameter is indeed no longer meaningful in practice, then deprecating it would also make sense.
I'm not quite clear when or why one would use the different values of the parameter. I think thinking this through exactly is the prerequisite for both documenting and deprecating (the latter because we must be sure we don't remove a valid use case).
Semi-related: The apply_aspect(position=) parameter is related to the topic and also not useful. We might be able to improve on that as well.
This one appears to be about colorbar placement. Of the four tests that fail, the only one obviously using colorbars is an image test with this reference.
The colorbar one seems to be only used for cases where the colorbar isn't drawn. The line right above sets the axes to invisible. The line in question is setting the axes to be tiny and in the corner of the figure. Perhaps that was necessary at the time to prevent the invisible axes affecting bbox_inches='tight'? These days setting invisible is enough to ensure an axes doesn't participate in layout questions.
In the parasite axes code, apply_aspect gets called right after set_position. Parasite axes does not appear to have its own apply_aspect, so I think the active position will immediate be overwritten by matplotlib.Axes.apply_aspect, as described in the OP.
Secondary axes and axes3d both have their own apply_aspect but again they always set the active position
I think deprecating this makes sense. We don't honour "active", so it doesn't make sense to offer it.
Long term removal may take some effort as there are various axes parameters that have the active and original positions, but I don't think that should change whether set_postion should make it seem like it has control over what type of position it sets.
Is this correct? - At its core the original/active concept is done because the Axes is originally placed somewhere. The eventually drawn active position may be different because of additional constraints (at least aspect handling, maybe also layout managers?). Once calculated, only the active position is relevant. It is used for drawing and also the default for get_position(). We only need the original position to be able to recalculate the active position if constraints change.
What does the user need?
setting and getting the original position - to be able to redesign the original position/arrangement of Axes
getting the active position - to know where the Axes is effectively so that they can put something next to it
Not needed: Setting the active position - this should always be calculated from the original position and the constraints.
On the one hand, both parts are needed, we can't make them go away. On the other hand, this feels a bit bolted on. Would it make more sense to have two separate functions? If I would redesign this from scratch, I'd likely use set_position()/get_position() only for the original position, and additionally introduce get_active_position() (likely with a better name).
The problem is that this is not compatible with the current defaults. In particular get_position returns the active position by default, but set_position by default sets both. Well, I suppose effectively only the original as the active should be recomputed, but t.b.d.
While we may be able to remove the parameter from set_position and reduce it effectively to set_position(..., which="original") in a compatible way for default usage. This deepens the inconsistency that set_position would apply to original, but get_position would apply to active. I have no good solution for that, maybe it's still the way to go, but requires very good documentation.
I think the history here is that set_position used to be used internally. We moved the actual function down to private and made set_position a wrapper that takes also the axes out of the layout management. Probably when that happened we should have removed the which parameter or started the deprecation.
Activity
[-][MNT]: deprecate *which* parameter of `Axes.set_position`?[/-][+][MNT]: deprecate _which_ parameter of `Axes.set_position`?[/+][-][MNT]: deprecate _which_ parameter of `Axes.set_position`?[/-][+][MNT]: deprecate "which" parameter of `Axes.set_position`?[/+]Chirag3841 commentedon Mar 8, 2026
@rcomer Clarifying the behavior in the docstring might be the safer option, since deprecating the
whichparameter could introduce an API change.rcomer commentedon Mar 8, 2026
Deprecations always introduce API changes. That is their purpose.
Chirag3841 commentedon Mar 8, 2026
I meant that clarifying the docstring could be a lower-impact first step, since it avoids starting a deprecation cycle immediately. If the parameter is indeed no longer meaningful in practice, then deprecating it would also make sense.
timhoffm commentedon Mar 8, 2026
I'm not quite clear when or why one would use the different values of the parameter. I think thinking this through exactly is the prerequisite for both documenting and deprecating (the latter because we must be sure we don't remove a valid use case).
Semi-related: The
apply_aspect(position=)parameter is related to the topic and also not useful. We might be able to improve on that as well.rcomer commentedon Mar 8, 2026
I tried adding the deprecation locally and found it is used twice in toolkits
matplotlib/lib/mpl_toolkits/axes_grid1/axes_grid.py
Lines 557 to 558 in 829a9cc
This one appears to be about colorbar placement. Of the four tests that fail, the only one obviously using colorbars is an image test with this reference.
matplotlib/lib/mpl_toolkits/axes_grid1/parasite_axes.py
Line 132 in 829a9cc
This one caused eight test failures, four of which are image comparison tests.
No tests fail if I simply remove those
set_positioncalls. So it seems like they are redundant, but I do not know theaxes_gridmodules very well.rcomer commentedon Mar 8, 2026
Looking again at those toolkits cases
bbox_inches='tight'? These days setting invisible is enough to ensure an axes doesn't participate in layout questions.apply_aspectgets called right afterset_position. Parasite axes does not appear to have its ownapply_aspect, so I think the active position will immediate be overwritten bymatplotlib.Axes.apply_aspect, as described in the OP.Secondary axes and axes3d both have their own
apply_aspectbut again they always set the active positionmatplotlib/lib/matplotlib/axes/_secondary_axes.py
Lines 144 to 147 in 829a9cc
matplotlib/lib/mpl_toolkits/mplot3d/axes3d.py
Lines 433 to 448 in 829a9cc
rcomer commentedon Mar 8, 2026
I found a third party case where a subclass clobbers
apply_aspect, so I guess setting the active position could work with that.set_positiondoes not appear in that repo though.https://github.com/AudioSceneDescriptionFormat/splines/blob/4473d4b3acc1c9abeb22197c50ec942566c7696a/doc/rotation/helper.py#L279-L280
jklymak commentedon Mar 8, 2026
I think deprecating this makes sense. We don't honour "active", so it doesn't make sense to offer it.
Long term removal may take some effort as there are various axes parameters that have the active and original positions, but I don't think that should change whether
set_postionshould make it seem like it has control over what type of position it sets.timhoffm commentedon Mar 9, 2026
Is this correct? - At its core the original/active concept is done because the Axes is originally placed somewhere. The eventually drawn active position may be different because of additional constraints (at least aspect handling, maybe also layout managers?). Once calculated, only the active position is relevant. It is used for drawing and also the default for
get_position(). We only need the original position to be able to recalculate the active position if constraints change.What does the user need?
On the one hand, both parts are needed, we can't make them go away. On the other hand, this feels a bit bolted on. Would it make more sense to have two separate functions? If I would redesign this from scratch, I'd likely use
set_position()/get_position()only for the original position, and additionally introduceget_active_position()(likely with a better name).The problem is that this is not compatible with the current defaults. In particular
get_positionreturns the active position by default, butset_positionby default sets both. Well, I suppose effectively only the original as the active should be recomputed, but t.b.d.While we may be able to remove the parameter from
set_positionand reduce it effectively toset_position(..., which="original")in a compatible way for default usage. This deepens the inconsistency thatset_positionwould apply to original, butget_positionwould apply to active. I have no good solution for that, maybe it's still the way to go, but requires very good documentation.jklymak commentedon Mar 9, 2026
I think the history here is that set_position used to be used internally. We moved the actual function down to private and made set_position a wrapper that takes also the axes out of the layout management. Probably when that happened we should have removed the which parameter or started the deprecation.
DOC: Document that set_aspect applies the aspect lazily
DOC: Document that set_aspect applies the aspect lazily
DOC: Document that set_aspect applies the aspect lazily