Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit89694ed

Browse files
committed
Prevent reuse of certain Locators and Formatters over multiple Axises.
The call to _wrap_locator_formatter was deleted from ThetaAxis.gca()because it is redundant with the call in ThetaAxis._set_scale (whichgca() calls), and would cause double wrapping with _AxisWrapper, causingissues when checking for axis equality.
1 parentac07e81 commit89694ed

File tree

4 files changed

+83
-7
lines changed

4 files changed

+83
-7
lines changed
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
Axis-dependent Locators and Formatters explicitly error out when used over multiple Axis
2+
````````````````````````````````````````````````````````````````````````````````````````
3+
4+
Certain Locators and Formatters (e.g. the default `AutoLocator` and
5+
`ScalarFormatter`) can only be used meaningfully on one Axis object at a time
6+
(i.e., attempting to use a single `AutoLocator` instance on the x and the y
7+
axis of an Axes, or the x axis of two different Axes, would result in
8+
nonsensical results).
9+
10+
Such "double-use" is now detected and raises a RuntimeError *at canvas draw
11+
time*. The exception is not raised when the second Axis is registered in order
12+
to avoid incorrectly raising exceptions for the Locators and Formatters that
13+
*can* be used on multiple Axis objects simultaneously (e.g. `NullLocator` and
14+
`FuncFormatter`).

‎lib/matplotlib/projections/polar.py

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,14 @@ class _AxisWrapper(object):
198198
def__init__(self,axis):
199199
self._axis=axis
200200

201+
def__eq__(self,other):
202+
# Needed so that assignment, as the locator.axis attribute, of another
203+
# _AxisWrapper wrapping the same axis works.
204+
returnself._axis==other._axis
205+
206+
def__hash__(self):
207+
returnhash((type(self),tuple(sorted(vars(self).items()))))
208+
201209
defget_view_interval(self):
202210
returnnp.rad2deg(self._axis.get_view_interval())
203211

@@ -227,10 +235,11 @@ class ThetaLocator(mticker.Locator):
227235
"""
228236
def__init__(self,base):
229237
self.base=base
230-
self.axis=self.base.axis=_AxisWrapper(self.base.axis)
238+
self.set_axis(self.base.axis)
231239

232240
defset_axis(self,axis):
233-
self.axis=_AxisWrapper(axis)
241+
super().set_axis(_AxisWrapper(axis))
242+
self.base._set_axises=set()# Bypass prevention of axis resetting.
234243
self.base.set_axis(self.axis)
235244

236245
def__call__(self):
@@ -383,7 +392,6 @@ def _wrap_locator_formatter(self):
383392
defcla(self):
384393
super().cla()
385394
self.set_ticks_position('none')
386-
self._wrap_locator_formatter()
387395

388396
def_set_scale(self,value,**kwargs):
389397
super()._set_scale(value,**kwargs)

‎lib/matplotlib/tests/test_ticker.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -897,3 +897,29 @@ def minorticksubplot(xminor, yminor, i):
897897
minorticksubplot(True,False,2)
898898
minorticksubplot(False,True,3)
899899
minorticksubplot(True,True,4)
900+
901+
902+
deftest_multiple_assignment():
903+
fig=plt.figure()
904+
905+
ax=fig.subplots()
906+
fmt=mticker.NullFormatter()
907+
ax.xaxis.set_major_formatter(fmt)
908+
ax.yaxis.set_major_formatter(fmt)
909+
fig.canvas.draw()# No error.
910+
fig.clf()
911+
912+
ax=fig.subplots()
913+
fmt=mticker.ScalarFormatter()
914+
ax.xaxis.set_major_formatter(fmt)
915+
ax.xaxis.set_minor_formatter(fmt)
916+
fig.canvas.draw()# No error.
917+
fig.clf()
918+
919+
ax=fig.subplots()
920+
fmt=mticker.ScalarFormatter()
921+
ax.xaxis.set_major_formatter(fmt)
922+
ax.yaxis.set_major_formatter(fmt)
923+
withpytest.raises(RuntimeError):
924+
fig.canvas.draw()
925+
fig.clf()

‎lib/matplotlib/ticker.py

Lines changed: 32 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -217,11 +217,39 @@ def get_tick_space(self):
217217
return9
218218

219219

220-
classTickHelper(object):
221-
axis=None
220+
classTickHelper:
221+
# TickHelpers that access their axis attribute can only be assigned to
222+
# one Axis at a time, but we don't know a priori whether they will (e.g.,
223+
# NullFormatter doesn't, but ScalarFormatter does). So keep track of all
224+
# Axises that a TickHelper is assigned to (in a set: a TickHelper could be
225+
# assigned both as major and minor helper on a single axis), but only error
226+
# out after multiple assignment when the attribute is accessed.
222227

223-
defset_axis(self,axis):
224-
self.axis=axis
228+
@property
229+
defaxis(self):
230+
# We can't set the '_set_axises' attribute in TickHelper.__init__
231+
# (without a deprecation period) because subclasses didn't have to call
232+
# super().__init__ so far so they likely didn't.
233+
set_axises=getattr(self,"_set_axises",set())
234+
iflen(set_axises)==0:
235+
returnNone
236+
eliflen(set_axises)==1:
237+
axis,=set_axises
238+
returnaxis
239+
else:
240+
print(set_axises)
241+
raiseRuntimeError(
242+
f"The 'axis' attribute of this{type(self).__name__} object "
243+
f"has been set multiple times, but a{type(self).__name__} "
244+
f"can only be used for one Axis at a time")
245+
246+
@axis.setter
247+
defaxis(self,axis):
248+
ifnothasattr(self,"_set_axises"):
249+
self._set_axises=set()
250+
self._set_axises.add(axis)
251+
252+
set_axis=axis.fset
225253

226254
defcreate_dummy_axis(self,**kwargs):
227255
ifself.axisisNone:

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp