GeezerVison is showing github.com — FixedLocator: copy ndarray input instead of aliasing it by pranjalm37 · Pull Request #32367 · matplotlib/matplotlib
Conversation
Fixes #32313.
Root cause
FixedLocator.__init__ stores its tick locations via np.asarray(locs):
def __init__(self, locs, nbins=None):
self.locs = np.asarray(locs)
np.asarray is a no-op when its input is already an ndarray — it returns the same array object, not a copy. Since Axis.set_ticks()/set_xticks() build a FixedLocator directly from whatever array the caller passed in, FixedLocator.locs ends up aliasing that array. Any later in-place mutation of it retroactively moves the axis' already-set tick locations:
import numpy as np
import matplotlib.pyplot as plt
fig, ax = plt.subplots()
theta = np.linspace(0, 2 * np.pi, 5, endpoint=False)
ax.set_xticks(theta)
theta += 2 * np.pi # unrelated in-place mutation elsewhere in the program
# ax's ticks silently moved too
A plain list input doesn't show this, since converting a list to an array always produces a fresh one — which is presumably why this went unnoticed for a while.
Fix
Use np.array(locs) instead, which copies by default, so FixedLocator owns its storage regardless of whether the input was a list or an existing ndarray.
Testing
- Added
TestFixedLocator.test_locs_is_a_copy_of_ndarray_input, which constructs a FixedLocator from an ndarray, mutates the array in place afterward, and asserts .locs is unaffected. Verified it fails on unpatched ticker.py (fixed.locs is not locs → AssertionError) and passes with the fix.
- Manually reproduced the original report's polar-plot example end-to-end (prebuilt
matplotlib wheel with just ticker.py swapped in, since I couldn't build the C extensions from source in my sandbox — no network access for the bundled freetype/etc. fetch): confirmed ax.get_xticks() changes after mutating the input array on unpatched code, and stays stable with the fix. Also confirmed a plain-list set_xticks([1, 2, 3]) call is unaffected.
ruff check on both changed files — clean (matplotlib's pyproject.toml only configures ruff check, not ruff format, so I didn't run the formatter).
AI disclosure
I used Claude Code to investigate and draft this fix; I reviewed the diagnosis (traced set_ticks → FixedLocator.__init__ → np.asarray), verified the reproduction and fix behavior myself as described above, and reviewed every changed line before opening this PR.
🤖 Generated with Claude Code
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
FixedLocator.__init__ stored locs via np.asarray(locs), which is a
no-op for an already-ndarray input -- it returns the same object
rather than a copy. Since set_ticks()/set_xticks() build their
FixedLocator directly from whatever array the caller passed, any
later in-place mutation of that array (e.g. `theta[i] += ...`) moved
the axis' already-set tick locations too, silently changing the plot.
A plain list input didn't have this problem, since converting a list
always produces a fresh array.
Use np.array(locs) instead, which copies by default, so FixedLocator
owns its own storage regardless of the input type.
Fixes matplotlib#32313.
|
Thank you for opening your first PR into Matplotlib!
If you have not heard from us in a week or so, please leave a new comment below and that should bring it to our attention. Most of our reviewers are volunteers and sometimes things fall through the cracks. We also ask that you please finish addressing any review comments on this PR and wait for it to be merged (or closed) before opening a new one, as it can be a valuable learning experience to go through the review process.
You can also join us on discourse chat for real-time discussion.
For details on testing, writing docs, and our review process, please see the developer guide.
Please let us know if (and how) you use AI, it will help us give you better feedback on your PR.
We strive to be a welcoming and open project. Please follow our Code of Conduct.
|
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
rcomer
commented
Sep 18, 2026
•
edited
Loading
Uh oh!
There was an error while loading. Please reload this page.
|
Hi @pranjalm37 and thank you for your interest in improving Matplotlib. From your PR description, it does not look like you read the comments on the issue. Please read those comments and say why you think this fix is still necessary in the light of them.
|
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
|
You're right, and I didn't read the issue thread before opening this. I only looked at the reproduction.
Having read it now: Tim's point stands. Copying in FixedLocator only covers one layer, and the same aliasing exists where set_ticks converts units and for anyone constructing FixedLocator directly, so a real fix means extra copies in several places for behavior that isn't guaranteed anyway. I don't have a justification for merging this on its own, so I'm closing it. Sorry for the noise.
|
rcomer reacted with thumbs up emoji
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Assignees
No one assigned
Milestone
No milestone
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
|