| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
|
The cleanup is reasonable. However, I'm stumbling a bit about the changed semantics. format_ticks() does not obviously imply that the internal state is changed (or that it even exists). set_locs() was explicit about that. Can we get a more clear API? One can have two views on the formatter:
fmt.format_ticks(values) feels like the simple formatter, but actually is an aligned formatter. It may be surprising that fmt.format_ticks(values) != [fmt.format_ticks([v])[0] for v in values]. Do we actually need the state? If not one could do something like def format_ticks(values, context=None):
""""
Format *values* in a consistent way.
The output format of a single value depends on the values in *context*.
If not given, the context are the values themselves.
""""
if context is None:
context = values
These are just quick thoughts so far. They probably need a bit more consideration. |
Sorry, something went wrong.
|
We already pass the state - thats how ticks = [1, 1.125, 1.25, 1.5] are labeled with the same precision (1.00, 1.125, 1.250, 1.500). Its really makes little sense to not just format all the ticks at once. #10682 is a concrete example of where this is even more useful, and you can think of others. This PR just makes that logic less convoluted. |
Sorry, something went wrong.
| (`~Formatter.set_locs` followed by repeated `~Formatter.__call__`) have been | ||
| updated to use `~Formatter.format_ticks`. | ||
|
|
||
| `~Formatter.format_tics` is intended to be overridden by `Formatter` subclasses |
There was a problem hiding this comment.
tics->ticks
Sorry, something went wrong.
There was a problem hiding this comment.
thanks, edited
Sorry, something went wrong.
|
The issue is that we secretly introduce state. fmt.set_locs(values) a = fmt.format_data_short(v) b = fmt.format_ticks(values) c = fmt.format_data_short(v) is explicit and a and c will have the same result. However, after this PR is in, the code would be a = fmt.format_data_short(v) b = fmt.format_ticks(values) c = fmt.format_data_short(v) and a and c may be different. That doesn't look like a sane API to me. |
Sorry, something went wrong.
There was a problem hiding this comment.
Sorry, but I want to block until the API is more clear. I'm quite sure that there is a better way than having set_locs separate from the formatting, but I have to look into this in more detail.
Sorry, something went wrong.
|
Are there contexts where you would set the locs, but not format the ticks? If not, I'm fine with format_data_short having a dependence on what the major ticks are... |
Sorry, something went wrong.
|
As @jklymak mentioned, hoping that ticks can be formatted independently from one another is a long-lost battle; there are pretty good reasons why they're interdependent. |
Sorry, something went wrong.
|
I agree that the required tick format must depends on all ticks.
If we get this to work, I'm fine with the present PR, because then the hidden state magic will be gone. I want to look into this before dismissing my change request. |
Sorry, something went wrong.
All calls to Formatter.set_locs occur before a call to Formatter.format_ticks (or its predecessor, a list of calls to `Formatter.__call__`); it was basically a bandaid around the fact that all tick locations were needed to properly format an individual tick. Move the call to set_locs into format_ticks, with the intention to later deprecate set_locs. Convert a few more places to use format_ticks.
|
Just to clarify; are you saying you want the other PR (which doesn't exist right now, we're just discussing design) to be made before unblocking this one? |
Sorry, something went wrong.
|
Not necessarily. I want to be sure that it can be done and I would like to have it at least soon after this one. |
Sorry, something went wrong.
|
Actually, the easiest is probably just to lookup the tick locations on the axis instance rather than passing it in as argument (otherwise that's what you'll have to do at the call site anyways...), so something like def format_data_short(self, value): # May or may not go through a rename deprecation cycle.
# or def format_data_short(self, value, ticklocs=None):
"""
Return a short string version of the tick value.
Defaults to the position-independent long value.
"""
# Actually, needs to know whether we are the major or minor formatter,
# but that's just a matter of `self is self.major.formatter`.
self.set_locs(self.axis.get_majorticklocs())
# or
self.set_locs(ticklocs if ticklocs is not None
else self.axis.get_majorticklocs())
return self.format_data(value)
then slowly move through the different formatter classes to make them independent of set_locs. |
Sorry, something went wrong.
|
Hm, the dependence on the axis is another hidden state, and may actually cause incorrect behavior: formatter = FuncFormatter(lambda x, pos: '_%s_' % x) ax = plt.gca() ax.xaxis.set_major_formatter(formatter) ax.yaxis.set_major_formatter(formatter) ax.xaxis.get_major_formatter().axis == ax.xaxis # False! I wouldn't want to use the axis in the formatter unless really necessary. |
Sorry, something went wrong.
|
Well, again locators and formatters have known their axis since forever; I guess it would be reasonable to make set_axis raise an error if the axis has already been set (similarly to how artists cannot be reassigned from one axes to another). |
Sorry, something went wrong.
That would make sense. |
Sorry, something went wrong.
|
Well, except that ThetaLocator is of course doing funky stuff like class ThetaLocator(mticker.Locator):
def __init__(self, base):
self.base = base
self.axis = self.base.axis = _AxisWrapper(self.base.axis)
def set_axis(self, axis):
self.axis = _AxisWrapper(axis)
self.base.set_axis(self.axis)
Probably can easily be worked around, but will take a bit longer than expected. |
Sorry, something went wrong.
|
Also, there's a bunch of formatters which actually do not care about the axis (NullFormatter, FixedFormatter, FuncFormatter, FormatStrFormatter, StrMethodFormatter) and these do get used across multiple axes in examples and tests; it also seems like gratuitious breakage to forbid their sharing :/ |
Sorry, something went wrong.
Target state:
Edit: Implementation detail:To maintain the major/minor aspect, formatter should have a flag if they are major or minor. The state should only contain the attibutes axis an is_major. Alternative B: Pass in the relevant Axis method loc_getter=axis.get_majorticklocs. Alternative C: Determine is_major = self.axis.major.formatter is self. I tend to favor alternative C, it's a bit indirect but does not store redundant information. |
Sorry, something went wrong.
|
Well, I'm not sure what's the best way forward with 2) given my comment just above. |
Sorry, something went wrong.
|
Can we make that optional? The axis is passed in to the formatter, but the formatter can decide not to use axis information, in which case it can be attached to multiple axes. The check for multiple connections would be in the formatter. Add a class-level flag uses_axis_context def set_axis(self, axis):
if not self.uses_axis_context:
return
elif: self.axis is not None:
raise ValueError('XYZFormatter cannot be attached to more than one axis/locator').
else:
self.axis = axis
|
Sorry, something went wrong.
|
OK, that's one possible design. Alternatively, we could make set_axis (.axis.setter) internally store a list of all axises this formatter has been assigned to, and error out when accessing the .axis attribute (if it has been set to multiple values), which would have the benefit of not requiring third-party formatters to change, and staying automatically correct for all formatter classes. However that'll likely be a separate PR (the problem of setting a formatter to multiple axises is not new). Marking this PR as release critical as it changes the semantics of the new format_ticks API and it would be much more of a pain to change this after it has been released. |
Sorry, something went wrong.
Unblocking. I think we're clear now, that the formatter should query the locs from the locator via the axis and not get them passed via set_locs. In that sense, this PR is a reasonable step to internalize getting the locations.
| Back | FazBrowse Home | New Git URL |
All calls to Formatter.set_locs occur before a call to
Formatter.format_ticks (or its predecessor, a list of calls to
Formatter.__call__); it was basically a bandaid around the fact that
all tick locations were needed to properly format an individual tick.
Move the call to set_locs into format_ticks, with the intention to later
deprecate set_locs.
Convert a few more places to use format_ticks.
Milestoning as 3.1 as format_ticks is a new API there so we can still change it before 3.1 is released...
PR Summary
PR Checklist