Skip to content

A "TypeError" is raised if subclass inherited from "datetime" is used #11275

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
Delgan opened this issue May 18, 2018 · 2 comments · Fixed by #13536
Closed

A "TypeError" is raised if subclass inherited from "datetime" is used #11275

Delgan opened this issue May 18, 2018 · 2 comments · Fixed by #13536

Comments

@Delgan
Copy link

Delgan commented May 18, 2018

Hello!

Bug summary

While trying to use matplotlib with custom subclass of datetime objects as x-values, a TypeError is raised.

It seems to exist a workaround using matplotlib.units: https://stackoverflow.com/a/10239347/2291710

However, I would like to know is it could be possible or not to support any subclass inherited from datetime natively (using issubclass() for example)? I tried to dig into the matplotlib source code but this is too complex for me.

The main use-case is for users of alternative datetime libraries like Pendulum or Arrow.

Code for reproduction

import datetime
import matplotlib.pyplot as plt

class MyDatetime(datetime.datetime):
    pass

values = range(4)
datetimes = [MyDatetime.now() for _ in values]

fig, ax = plt.subplots()
ax.plot(datetimes, values)

Actual outcome

Traceback (most recent call last):
  File "C:\Users\delgan\Desktop\test.py", line 11, in <module>
    ax.plot(datetimes, values)
  File "C:\Users\delgan\AppData\Local\Programs\Python\Python36\lib\site-packages\matplotlib\__init__.py", line 1710, in inner
    return func(ax, *args, **kwargs)
  File "C:\Users\delgan\AppData\Local\Programs\Python\Python36\lib\site-packages\matplotlib\axes\_axes.py", line 1438, in plot
    self.add_line(line)
  File "C:\Users\delgan\AppData\Local\Programs\Python\Python36\lib\site-packages\matplotlib\axes\_base.py", line 1759, in add_line
    self._update_line_limits(line)
  File "C:\Users\delgan\AppData\Local\Programs\Python\Python36\lib\site-packages\matplotlib\axes\_base.py", line 1781, in _update_line_limits
    path = line.get_path()
  File "C:\Users\delgan\AppData\Local\Programs\Python\Python36\lib\site-packages\matplotlib\lines.py", line 951, in get_path
    self.recache()
  File "C:\Users\delgan\AppData\Local\Programs\Python\Python36\lib\site-packages\matplotlib\lines.py", line 652, in recache
    x = _to_unmasked_float_array(xconv).ravel()
  File "C:\Users\delgan\AppData\Local\Programs\Python\Python36\lib\site-packages\matplotlib\cbook\__init__.py", line 2008, in _to_unmasked_float_array
    return np.asarray(x, float)
  File "C:\Users\delgan\AppData\Local\Programs\Python\Python36\lib\site-packages\numpy\core\numeric.py", line 531, in asarray
    return array(a, dtype, copy=False, order=order)
TypeError: float() argument must be a string or a number, not 'MyDatetime'

Expected outcome

There should be no error, and the graph should be plotted as if the dates was proper datetime objects.

Matplotlib version

  • Operating system: WSL
  • Matplotlib version: 2.1.0
  • Matplotlib backen: TkAgg
  • Python version: 3.6.4
@jklymak
Copy link
Member

jklymak commented May 19, 2018

Not the way units work right now. It uses a lookup table in units.py with the class types as keys, so I'm not sure there is any way to make that check parent classes as well.

To get what you want, you can just do:

import datetime
import matplotlib.pyplot as plt

import matplotlib.dates as mdates
import matplotlib.units as munits

class MyDatetime(datetime.datetime):
    pass
munits.registry[MyDatetime] = mdates.DateConverter()

values = range(4)
datetimes = [MyDatetime.now() for _ in values]
print(datetimes)
fig, ax = plt.subplots()
ax.plot(datetimes, values)

I'll leave open in case anyone has another idea, but the way units work, this isn't likely to change....

@anntzer
Copy link
Contributor

anntzer commented May 19, 2018

It should be relatively easy to make the unit registry walk the parents classes. Would be nice to get #9314 in as a first simplification though...

Edit: to be specific, I think something like the following patch on top of #9314 would suffice:

diff --git a/lib/matplotlib/units.py b/lib/matplotlib/units.py
index dd52e4835..6df15ee88 100644
--- a/lib/matplotlib/units.py
+++ b/lib/matplotlib/units.py
@@ -140,8 +140,8 @@ class Registry(dict):
             # the array itself.
             x = np.ma.getdata(x).ravel()
         try:
-            return self[type(x)]
-        except KeyError:
+            return next(self[cls] for cls in type(x).__mro__ if cls in self)
+        except StopIteration:
             try:
                 first = safe_first_element(x)
             except (TypeError, StopIteration):

(plus tests, of course)

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

Successfully merging a pull request may close this issue.

4 participants