Skip to content

[MNT]: deprecate "which" parameter of Axes.set_position? #31254

Closed

Description

@rcomer

Summary

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.

Activity

  1. changed the title [-][MNT]: deprecate *which* parameter of `Axes.set_position`?[/-] [+][MNT]: deprecate _which_ parameter of `Axes.set_position`?[/+] on Mar 7, 2026
  2. changed the title [-][MNT]: deprecate _which_ parameter of `Axes.set_position`?[/-] [+][MNT]: deprecate "which" parameter of `Axes.set_position`?[/+] on Mar 7, 2026
  3. Chirag3841 commented on Mar 8, 2026

    @Chirag3841
    Contributor

    @rcomer Clarifying the behavior in the docstring might be the safer option, since deprecating the which parameter could introduce an API change.

  4. rcomer commented on Mar 8, 2026

    @rcomer
    MemberAuthor

    Deprecations always introduce API changes. That is their purpose.

  5. Chirag3841 commented on Mar 8, 2026

    @Chirag3841
    Contributor

    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.

  6. timhoffm commented on Mar 8, 2026

    @timhoffm
    Member

    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.

  7. rcomer commented on Mar 8, 2026

    @rcomer
    MemberAuthor

    I tried adding the deprecation locally and found it is used twice in toolkits

    self.cbar_axes[i].set_position([1., 1., 0.001, 0.001],
    which="active")

    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.

    self.set_position(pos, which="active")

    This one caused eight test failures, four of which are image comparison tests.

    No tests fail if I simply remove those set_position calls. So it seems like they are redundant, but I do not know the axes_grid modules very well.

  8. rcomer commented on Mar 8, 2026

    @rcomer
    MemberAuthor

    Looking again at those toolkits cases

    • 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

    def apply_aspect(self, position=None):
    # docstring inherited.
    self._set_lims()
    super().apply_aspect(position)

    def apply_aspect(self, position=None):
    if position is None:
    position = self.get_position(original=True)
    # in the superclass, we would go through and actually deal with axis
    # scales and box/datalim. Those are all irrelevant - all we need to do
    # is make sure our coordinate system is square.
    trans = self.get_figure().transSubfigure
    bb = mtransforms.Bbox.unit().transformed(trans)
    # this is the physical aspect of the panel (or figure):
    fig_aspect = bb.height / bb.width
    box_aspect = 1
    pb = position.frozen()
    pb1 = pb.shrunk_to_aspect(box_aspect, pb, fig_aspect)
    self._set_position(pb1.anchored(self.get_anchor(), pb), 'active')

  9. rcomer commented on Mar 8, 2026

    @rcomer
    MemberAuthor

    I found a third party case where a subclass clobbers apply_aspect, so I guess setting the active position could work with that. set_position does not appear in that repo though.
    https://github.com/AudioSceneDescriptionFormat/splines/blob/4473d4b3acc1c9abeb22197c50ec942566c7696a/doc/rotation/helper.py#L279-L280

  10. jklymak commented on Mar 8, 2026

    @jklymak
    Member

    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.

  11. timhoffm commented on Mar 9, 2026

    @timhoffm
    Member

    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.

  12. jklymak commented on Mar 9, 2026

    @jklymak
    Member

    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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions