Thanks to visit codestin.com
Credit goes to github.com

Skip to content

WIP: Lockout new converters Part 2 #9776

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 24 additions & 8 deletions lib/matplotlib/axes/_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,10 @@
from six.moves import xrange

import itertools
import warnings
import logging
import math
from operator import attrgetter
import warnings

import numpy as np

Expand Down Expand Up @@ -38,6 +39,8 @@
from matplotlib.rcsetup import cycler
from matplotlib.rcsetup import validate_axisbelow

_log = logging.getLogger(__name__)

rcParams = matplotlib.rcParams

is_string_like = cbook.is_string_like
Expand Down Expand Up @@ -1002,7 +1005,7 @@ def cla(self):
else:
self.xaxis._set_scale('linear')
try:
self.set_xlim(0, 1)
self.set_xlim(0, 1, _converter=False)
except TypeError:
pass

Expand All @@ -1016,7 +1019,7 @@ def cla(self):
else:
self.yaxis._set_scale('linear')
try:
self.set_ylim(0, 1)
self.set_ylim(0, 1, _converter=False)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can kwargs be private? (I've never seen this before so I have no idea)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

From a philosophical standpoint, nothing can be private in the panopticon that is Python symbol visibility, but here is an example from pytz of a private kwarg (though I think popping it from **kwargs so it doesn't show up under inspection is a better course to take).

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it is pretty clear that a kwarg with an underscore is intended as private.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe the better question is, will an underscored kwarg appear in the documentation? (I'm not convinced in this case that the kwarg is needed anyway)

Copy link
Member

@pganssle pganssle Dec 28, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@dstansby Looks to me like the way it's implemented, it's just being pulled from the kwargs dictionary, it's not an actual declared keyword argument, so you'd need a pretty smart document generator to even figure out that passing _converter to this would be meaningful.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just to justify this kwarg - the point of this PR is that the first time you conver units on an axis, the converter gets set. This happens on set_xticks, set_xlims or any of the plotting calls.

cla calls set_xlim but its not doing so because it has any data that might have units. So this kwarg is just an easy way to say "set the limit, but don't assign the units because this is just a placeholder".

We could, alternately, make a new method (_set_xlim_placeholder) that did the same thing, if that is better than a hidden kwarg?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think either way is fine.

except TypeError:
pass

Expand Down Expand Up @@ -2377,6 +2380,7 @@ def autoscale_view(self, tight=None, scalex=True, scaley=True):
case, use :meth:`matplotlib.axes.Axes.relim` prior to calling
autoscale_view.
"""
_log.debug('Autoscaling x: %s y: %s', scalex, scaley)
if tight is not None:
self._tight = bool(tight)

Expand Down Expand Up @@ -2446,6 +2450,7 @@ def handle_single_axis(scale, autoscaleon, shared_axes, interval,

if not self._tight:
x0, x1 = locator.view_limits(x0, x1)
_log.debug('Autolims: %e %e', x0, x1)
set_bound(x0, x1)
# End of definition of internal function 'handle_single_axis'.

Expand Down Expand Up @@ -2923,6 +2928,7 @@ def set_xbound(self, lower=None, upper=None):
self.set_xlim(upper, lower, auto=None)
else:
self.set_xlim(lower, upper, auto=None)
self.set_xlim(lower, upper, auto=None)
else:
if lower < upper:
self.set_xlim(lower, upper, auto=None)
Expand Down Expand Up @@ -3028,15 +3034,19 @@ def set_xlim(self, left=None, right=None, emit=True, auto=False, **kw):
left = kw.pop('xmin')
if 'xmax' in kw:
right = kw.pop('xmax')
# _converter is private, usually True, but can be false
_converter = kw.pop('_converter', True)
if kw:
raise ValueError("unrecognized kwargs: %s" % list(kw))

if right is None and iterable(left):
left, right = left

self._process_unit_info(xdata=(left, right))
left = self._validate_converted_limits(left, self.convert_xunits)
right = self._validate_converted_limits(right, self.convert_xunits)
if _converter:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the purpose of this if statement?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As above. Its because cla needs the set_x/ylim call. I tried getting rid of it, but bad things seemed to happen.

_log.debug('Converting xlim %s %s', left, right)
self._process_unit_info(xdata=(left, right))
left = self._validate_converted_limits(left, self.convert_xunits)
right = self._validate_converted_limits(right, self.convert_xunits)

old_left, old_right = self.get_xlim()
if left is None:
Expand Down Expand Up @@ -3352,14 +3362,20 @@ def set_ylim(self, bottom=None, top=None, emit=True, auto=False, **kw):
bottom = kw.pop('ymin')
if 'ymax' in kw:
top = kw.pop('ymax')
_converter = kw.pop('_converter', True)
if kw:
raise ValueError("unrecognized kwargs: %s" % list(kw))

if top is None and iterable(bottom):
bottom, top = bottom

bottom = self._validate_converted_limits(bottom, self.convert_yunits)
top = self._validate_converted_limits(top, self.convert_yunits)
if _converter:
_log.debug('Converting ylim %s %s', bottom, top)
self._process_unit_info(ydata=(bottom, top))
bottom = self._validate_converted_limits(bottom,
self.convert_yunits)
top = self._validate_converted_limits(top,
self.convert_yunits)

old_bottom, old_top = self.get_ylim()

Expand Down
23 changes: 22 additions & 1 deletion lib/matplotlib/axis.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import six

import logging
import warnings

from matplotlib import rcParams
import matplotlib.artist as artist
Expand All @@ -22,7 +23,8 @@
import matplotlib.transforms as mtransforms
import matplotlib.units as munits
import numpy as np
import warnings

_log = logging.getLogger(__name__)

_log = logging.getLogger(__name__)

Expand Down Expand Up @@ -1434,10 +1436,25 @@ def update_units(self, data):
if *data* is registered for unit conversion.
"""

_log.debug('update_units converter due to %s %s',
data, data.__class__,)
converter = munits.registry.get_converter(data)
_log.debug('new converter: %s', converter)
if converter is None:
_log.info('No converter found for this data')
return False

if self.converter is not None:
if self.converter == converter:
_log.debug('Axis already has a converter that matches '
'data units')
return True
else:
_log.info('Axis already has a converter, but it does '
'not match new data units.')
return False


neednew = self.converter != converter
self.converter = converter
default = self.converter.default_units(data, self)
Expand Down Expand Up @@ -1487,6 +1504,10 @@ def have_units(self):
return self.converter is not None or self.units is not None

def convert_units(self, x):
"""
convert the units given
"""

if self.converter is None:
self.converter = munits.registry.get_converter(x)

Expand Down
1 change: 1 addition & 0 deletions lib/matplotlib/dates.py
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,7 @@
from six.moves import zip
import re
import time
import logging
import math
import datetime
import functools
Expand Down
11 changes: 11 additions & 0 deletions lib/matplotlib/tests/test_dates.py
Original file line number Diff line number Diff line change
Expand Up @@ -607,3 +607,14 @@ def test_tz_utc():
def test_num2timedelta(x, tdelta):
dt = mdates.num2timedelta(x)
assert dt == tdelta


def test_one_non_None_converter():
# test that we only allow one non-None converter at once:
base = datetime.datetime(2017, 1, 1, 0, 10, 0)
time = [base - datetime.timedelta(days=x) for x in range(0, 3)]
data = [0., 2., 4.]
fig, ax = plt.subplots()
ax.plot(time, data)
with pytest.raises(AttributeError):
ax.plot(['a', 'b'], [1., 2.])
21 changes: 21 additions & 0 deletions lib/matplotlib/units.py
Original file line number Diff line number Diff line change
Expand Up @@ -171,4 +171,25 @@ def get_converter(self, x):
return converter


class DefaultConverter(ConversionInterface):
"""
Do-nothing converter to catch good entries and lock out other
converters.
"""
@staticmethod
def convert(obj, unit, axis):
"""
convert obj using unit for the specified axis. If obj is a sequence,
return the converted sequence. The output must be a sequence of
scalars that can be used by the numpy array layer
"""
return np.asarray(obj, dtype=np.float64)


registry = Registry()
registry[np.float64] = DefaultConverter()
registry[np.float32] = DefaultConverter()
registry[np.int32] = DefaultConverter()
registry[np.int64] = DefaultConverter()
registry[float] = DefaultConverter()
registry[int] = DefaultConverter()
17 changes: 11 additions & 6 deletions lib/mpl_toolkits/mplot3d/axes3d.py
Original file line number Diff line number Diff line change
Expand Up @@ -612,15 +612,17 @@ def set_xlim3d(self, left=None, right=None, emit=True, auto=False, **kw):
left = kw.pop('xmin')
if 'xmax' in kw:
right = kw.pop('xmax')
_converter = kw.pop('_converter', True)
if kw:
raise ValueError("unrecognized kwargs: %s" % list(kw))

if right is None and cbook.iterable(left):
left, right = left

self._process_unit_info(xdata=(left, right))
left = self._validate_converted_limits(left, self.convert_xunits)
right = self._validate_converted_limits(right, self.convert_xunits)
if _converter:
self._process_unit_info(xdata=(left, right))
left = self._validate_converted_limits(left, self.convert_xunits)
right = self._validate_converted_limits(right, self.convert_xunits)

old_left, old_right = self.get_xlim()
if left is None:
Expand Down Expand Up @@ -664,15 +666,18 @@ def set_ylim3d(self, bottom=None, top=None, emit=True, auto=False, **kw):
bottom = kw.pop('ymin')
if 'ymax' in kw:
top = kw.pop('ymax')
_converter = kw.pop('_converter', True)
if kw:
raise ValueError("unrecognized kwargs: %s" % list(kw))

if top is None and cbook.iterable(bottom):
bottom, top = bottom

self._process_unit_info(ydata=(bottom, top))
bottom = self._validate_converted_limits(bottom, self.convert_yunits)
top = self._validate_converted_limits(top, self.convert_yunits)
if _converter:
self._process_unit_info(ydata=(bottom, top))
bottom = self._validate_converted_limits(bottom,
self.convert_yunits)
top = self._validate_converted_limits(top, self.convert_yunits)

old_bottom, old_top = self.get_ylim()
if bottom is None:
Expand Down