Under matplotlib 3.0.0rc1, the return value of matplotlib.get_backend() is a sentinel object:
In [2]: matplotlib.get_backend()
Out[2]: <object at 0x111292d50>
In [3]: backend = matplotlib.get_backend()
In [4]: backend.lower()
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
<ipython-input-4-f3c7164fcd07> in <module>()
----> 1 backend.lower()
AttributeError: 'object' object has no attribute 'lower'
Under matplotlib 2.2.3 I get:
In [1]: import matplotlib
In [2]: matplotlib.get_backend()
Out[2]: 'MacOSX'
In [3]: backend = matplotlib.get_backend()
In [4]: backend.lower()
Out[4]: 'macosx'
For backward compatibility purposes it would be really nice if matplotlib 3.0.0 could return an object that at least acted like a string. In particular right now I'd like it to have a lower() method. I'd also like it if it had a nice __repr__ or __str__ implementation instead of printing out as an opaque object of type object.
The former is for backward compatibility, as the change breaks some of the yt plotting code --- in particular here's a failure from the test suite:
======================================================================
ERROR: Failure: AttributeError ('object' object has no attribute 'lower')
----------------------------------------------------------------------
Traceback (most recent call last):
File "/usr/local/lib/python3.7/site-packages/nose/failure.py", line 39, in runTest
raise self.exc_val.with_traceback(self.tb)
File "/usr/local/lib/python3.7/site-packages/nose/loader.py", line 418, in loadTestsFromName
addr.filename, addr.module)
File "/usr/local/lib/python3.7/site-packages/nose/importer.py", line 47, in importFromPath
return self.importFromDir(dir_path, fqname)
File "/usr/local/lib/python3.7/site-packages/nose/importer.py", line 94, in importFromDir
mod = load_module(part_fqname, fh, filename, desc)
File "/usr/local/Cellar/python/3.7.0/Frameworks/Python.framework/Versions/3.7/lib/python3.7/imp.py", line 235, in load_module
return load_source(name, filename, file)
File "/usr/local/Cellar/python/3.7.0/Frameworks/Python.framework/Versions/3.7/lib/python3.7/imp.py", line 172, in load_source
module = _load(spec)
File "<frozen importlib._bootstrap>", line 696, in _load
File "<frozen importlib._bootstrap>", line 677, in _load_unlocked
File "<frozen importlib._bootstrap_external>", line 728, in exec_module
File "<frozen importlib._bootstrap>", line 219, in _call_with_frames_removed
File "/Users/goldbaum/Documents/yt-git-fixes/yt/visualization/tests/test_color_maps.py", line 23, in <module>
class TestColorMaps(unittest.TestCase):
File "/Users/goldbaum/Documents/yt-git-fixes/yt/visualization/tests/test_color_maps.py", line 34, in TestColorMaps
@requires_backend('Agg')
File "/Users/goldbaum/Documents/yt-git-fixes/yt/testing.py", line 1143, in requires_backend
if backend.lower() == matplotlib.get_backend().lower():
AttributeError: 'object' object has no attribute 'lower'
This is code that's internal to yt's test suite, but I wouldn't be at all surprised if there's other code out there that assumes matplotlib.get_backend() returns a string. For example:
The issue is #11600 (comment) and I think (quite strongly) that it makes sense for get_backend() to, well, perform backend resolution and return an actual backend.
Giving the sentinel object a .lower() method is only going to make things worse IMO.
I don't really care too much what type get_backend returns. I'd kinda prefer it to return something stringlike just on the principle that it would be better if old code continued working, but I also acknowledge that you're doing a major version number increment so some level of breakage is to be expected.
I don't understand this. I am on v3.0.0rc1 on my git version but matplotlib.get_backend() returns a string for me and the docstring says that it should return the name.
That seems like a strange api to me. Why do it return an object when the backend is not set and not for example '' and a string when it is set? (that was the behavior that I got when I tested).
that it makes sense for get_backend() to, well, perform backend resolution and return an actual backend.
Why? The docstring for matplotlib.get_backend is "Return the name of the current backend."
The simplest fix may be to switch the sentinel to the string 'auto'? That way it can go in the .matplotlibrc and returns the expected type from get_backend()
I would prefer that if we keep the non-string sentinel that the backend resolution logic moves up to __init__ rather than adding another buried pyplot import.
Activity
anntzer commented on Aug 13, 2018
The issue is #11600 (comment) and I think (quite strongly) that it makes sense for get_backend() to, well, perform backend resolution and return an actual backend.
Giving the sentinel object a .lower() method is only going to make things worse IMO.
ngoldbaum commented on Aug 13, 2018
I don't really care too much what type get_backend returns. I'd kinda prefer it to return something stringlike just on the principle that it would be better if old code continued working, but I also acknowledge that you're doing a major version number increment so some level of breakage is to be expected.
fredrik-1 commented on Aug 14, 2018
I don't understand this. I am on v3.0.0rc1 on my git version but matplotlib.get_backend() returns a string for me and the docstring says that it should return the name.
anntzer commented on Aug 14, 2018
That's likely because this should only happen if you don't have a backend set in your matplotlibrc.
fredrik-1 commented on Aug 14, 2018
That seems like a strange api to me. Why do it return an object when the backend is not set and not for example '' and a string when it is set? (that was the behavior that I got when I tested).
Why? The docstring for matplotlib.get_backend is "Return the name of the current backend."
anntzer commented on Aug 14, 2018
As explained above, it's a bug (IMO) and should be fixed.
tacaswell commented on Aug 14, 2018
The simplest fix may be to switch the sentinel to the string 'auto'? That way it can go in the .matplotlibrc and returns the expected type from get_backend()
I would prefer that if we keep the non-string sentinel that the backend resolution logic moves up to __init__ rather than adding another buried pyplot import.
tacaswell commented on Aug 14, 2018
Closing this in favor of #11844 which is addressing a slightly larger set of issues to keep the discussion from getting too fragmented.