problem in cbook.py

Hi,

I am not expert in matplotlib, I use it to generate plots
for a management performance tool.

I have just upgraded this application from 0.98.3 to 0.98.5 and
the upgrade broke the application in 3 places:

1). In units.py, Registry.get_converter(): this
method has been made recursive in 0.98.5. The problem is that if x is a
string then you get a recursion error.

The cause is the ‘iterable() function called in the
line “if converter is None and interable(x):” The interable() function
is in cbook.py and it actually tests to see if the object ‘x’ has a
len() method. Call iterable() on a string and it returns true.

Do “ for thisx in x:” and you will always get
the first character in the string, which is… a string!

My solution was to change iterable():

Def iterable(obj):

“””

Return true if obj is
iterable

“””

try: obj.iter()

except AtributeError: return
False

return True

this seems to have solved the problem.

2). Backend_svg.py._get_style(): in 0.98.3,
stroke_width is a string and in 0.98.5 it is a float. I set the styles by
reading attributes in an xml file and so the strings worked. When I upgraded, I
got ‘float’ type errors. My opinion is that we might expect
parametric polymorphism here. A stroke width could be ‘1’, 1 or 1.0
all of which are legitimate parameters. Hence, when we set the kwarg ‘lw’
on ‘plot’, a string, int or float should be legal. Just my opinion.

3). Font_manager.createFontDict() in 0.98.3 become createFontList()
in 0.98.5. I didn’t find this change in Changes, but maybe I’m
blind. What I am saying here is that if I upgrade to 0.98.5 from 0.98.3, I
would expect 0.98.5 to be less ‘broke’ than 0.98.3, not different.

I hope this is somehow helpful.

Best regards, Richard Jennings

I'm just commenting on the points I know about below -- leaving the rest to others.

Richard Jennings wrote:

2). Backend_svg.py._get_style(): in 0.98.3, stroke_width is a string and in 0.98.5 it is a float. I set the styles by reading attributes in an xml file and so the strings worked. When I upgraded, I got �float� type errors. My opinion is that we might expect parametric polymorphism here. A stroke width could be �1�, 1 or 1.0 all of which are legitimate parameters. Hence, when we set the kwarg �lw� on �plot�, a string, int or float should be legal. Just my opinion.

This change was made to work around a localization bug in Numpy. In a French locale (for example), printing a Numpy float scalar as a string would be "1,2" (which is against the SVG spec) and as a float would be "1.2".

But you're right -- this inadvertently broke supporting strings for certain parameters. I'm actually of the opinion that supporting strings in the first place was an unintentional feature -- none of our example or regression code uses it. I'm not sold that this is something that needs to be fixed.

But at a higher level, this re-raises the recurring issue about argument checking. In general, matplotlib doesn't do much type checking/coercion at the front end (user end), which leads to these sorts of unintended consequences in the backend. I would like to see more argument checking happening earlier so the user would get more localised and appropriate feedback when they passed in an invalid parameter. The idea of using Enthought's Traits library for this was thrown around in the past, but at the time it was impractical. Unfortunately, in the meantime that's kept us from doing the obvious and straightforward thing which is just to do more type and value checking in the setter methods. I think it would make a good project to go through and add type checkers to all the setters, followed by other user-points of entry, but it's not the sexiest or most interesting thing to do, perhaps.

3). Font_manager.createFontDict() in 0.98.3 become createFontList() in 0.98.5. I didn�t find this change in Changes, but maybe I�m blind. What I am saying here is that if I upgrade to 0.98.5 from 0.98.3, I would expect 0.98.5 to be less �broke� than 0.98.3, not different.

matplotlib version numbering has not traditionally followed Python's numbering scheme where the third number implies only bug fixes. We have started that sort of approach only recently with 0.98.5 where 0.98.5.x will be bugfixes only. In the future (maybe beginning 1.0 and later) we can probably have a more traditional version numbering scheme, though there has been no formal written decision on that.

That change has to do with moving from exact matching (which was "broke") to nearest-neighbor font matching. It is arguable that this is a bugfix, in that certain font lookups which were broken before are now correct, even though that implies different behavior.

Mike

···

--
Michael Droettboom
Science Software Branch
Operations and Engineering Division
Space Telescope Science Institute
Operated by AURA for NASA

This has been fixed on the SVN trunk. FYI, the problem with your fix above is that, in general, we want strings to be considered iterable – just not in this case. It should also be noted that the purpose of the fix was to eliminate the confusing infinite recursion. The fact that plotting strings of numbers works is a coincidence of how matplotlib is coded up and really should not be considered a supported feature.

Ryan

···

On Fri, Feb 13, 2009 at 3:24 AM, Richard Jennings <rpjennings@…287…> wrote:

Hi,

I am not expert in matplotlib, I use it to generate plots
for a management performance tool.

I have just upgraded this application from 0.98.3 to 0.98.5 and
the upgrade broke the application in 3 places:

1). In units.py, Registry.get_converter(): this
method has been made recursive in 0.98.5. The problem is that if x is a
string then you get a recursion error.

The cause is the 'iterable() function called in the
line “if converter is None and interable(x):” The interable() function
is in cbook.py and it actually tests to see if the object ‘x’ has a
len() method. Call iterable() on a string and it returns true.

Do " for thisx in x:" and you will always get
the first character in the string, which is… a string!

My solution was to change iterable():

Def iterable(obj):

“”"

Return true if obj is
iterable

“”"

try: obj.iter()

except AtributeError: return
False

return True

this seems to have solved the problem.


Ryan May
Graduate Research Assistant
School of Meteorology
University of Oklahoma
Sent from: Norman Oklahoma United States.