Repository: bokeh/bokeh

Issue:
Setting a fixed font size value as a string is deprecated warnings
I just installed bokeh 0.9.1 but now I get tons of "/Users/Marco/anaconda/lib/python3.4/site-packages/bokeh/properties.py:363: DeprecationWarning: Setting a fixed font size value as a string '10pt' is deprecated, set with value('10pt') or ['10pt'] instead" warnings.

I do set the font size using

```
p.axis.major_label_text_font_size = '8pt'
```

and due to the warning I tried to change this to 

```
p.axis.major_label_text_font_size = ['8pt']
```

but then I get no plot at all.

I tried to look at your examples but they all use the string version so I couldn't find any solution to this yet. What's the right way to set the font size now?

Thank you so much!

Marco



Discussion:
Hi @marcomayer looks like you found a little bug. The "list" syntax may go away anyway. You can set values like this, however:

```
from bokeh.properties import value
p.axis.major_label_text_font_size=value("8pt")
```

For a little context, the reason for the deprecation is that string values for data specs are, in every other context, interpreted as column data source column names. This changes brings consistency to the interface. 

thanks @bryevdv using value() this works fine. I couldn't figure out what "value" actually is since I never used it. I personally think it's a bit confusing and was fine before accepting strings but I just started working with bokeh and I'm sure you guys have good reasons for this.

And I think it would be nice if you would update the examples/tutorials to reflect such changes...it really makes it easier for someone new to bokeh to get started if the examples work and show the right way of usage.

@marcomayer it's probably something we need to document better. Basically there are two explicit functions:
- `value` marks something a a literal single value 
- `field` marks a string as referring to a column name (as opposed to being a literal data value)

Normally all of this is handled automatically. But there are a couple of places where it is ambiguous and this just makes things more consistent.

@bryevdv okay I see. But why would a text font size be ambiguous? I would never expect a column name here, at least not less as when setting p.title for example. This just doesn't sound very pythonic to me. I think it should accept the size as both a simple string or using value and maybe even as an int/float.

But I guess you have more ambiguous cases in other settings maybe and now try to unify things...

In general you might want to scatter a text glyph with a varying text size, perhaps for a word cloud. I agree for the "single use" properties (i.e. and title and not a glyph) it could potentially make sense. But we would have to implement something that distinguished more carefully between the two cases (right now the "properties" code is re-used in both ways) and we can't have it both ways. After some back-and-forth discussion it was decided that consistency is more important at this point. It's never good to have to write docs like "string values are always interpreted as field names..... well, except in this case and this case...." Having to remember what is an exception and what is not incurs its own cognitive burden and Bokeh is already large and alot to take in. 

Thanks for the questioning and discussion on this point, BTW. 

Thank you too! I already learned a lot :)

Okay that makes sense but it's not a very intuitive solution.

For example I can still do 

p.xaxis.major_label_text_color = 'red'

without getting a warning...which makes sense from a bokeh-coders perspective because it uses Color and not value but for the end-user it's quite confusing I think.

You raise a very good point. `ColorSpec` properties do perform some inspection and treat things that look like color values as color values. We might end up doing the same thing with text font sizes, it's just a more ambiguous thing to check for something that "looks like" a font size (there are _lots_ of "units")

Hi @marcomayer, I was definitely confused by this too at one point - and we had a good discussion about this here: https://github.com/bokeh/bokeh/pull/2449

Another thing to seperate in your mind - which maybe we need to do better in the docs - is `data powering charts` vs `configuring charts`

### Data powered charts

columns of data can be used to power almost all the properties of a Glyph - that's what's so cool about bokeh - i'll admit it seems like an unusual usecase to have a column of text sizes - but not unthinkable.

### Configuring charts

Things like axes, grid lines, plot properties can all be set, but can't be powered by data in the same way.

If you're building a chart from the models API you'll make a plot `p = Plot()` and then you'll add things to it using `p.add_glyph(datasource, glyph)` for glyphs and `p.add_tools(...` or `p.add_layout` this makes it more obvious which things are data powered. But when you use the higher level apis, this is a bit more obscured.

I think this is an interesting discussion - thanks for bringing it up.

@bryevdv yes maybe it's something to add in the future. I don't know how many formats there are for font sizes but checking if it's a string and if it's a string if it matches number+'pt' etc. might be a way to make this even better.

Hi @birdsarah thanks for your input! I think I'm starting to understand bokeh much better! 

One more issue I can't get solved that is related to this:

When I try to plot text using:

import bokeh.properties as prop

p = figure()
p.text(text='text', y=1, x=1, text_align = 'left', text_baseline='middle', text_font_size=prop.value('10pt'), text_color='red')
p.show()

I'll get the warning even while using value() explicitly. Also it's a good example that I think shows just how weird it looks (at least to me) to have to use value() just for the font size, but not for any of the other settings...

@marcomayer @bryevdv @birdsarah 

p.text(text='text', y=1, x=1, text_align = 'left', text_baseline='middle', text_font_size=prop.value('10pt'), text_color='red')

How do I write the code above that meets all these conditions?
1. without getting a warning.
2. being able to specify the font size.
3. Staying portable with the next upgrade.

@ankuagar The validation code is new, and there appear to be some bugs where it over-warns. There also appear to be some bugs with the use of `value` It may not be possible to get rid of the warning until a future release. `value('10pt')` should always work in the future, however. In the mean time you can use the "list syntax":

```
p.text(text=['foo'], y=1, x=1, text_align = 'left', text_baseline='middle', 
       text_font_size=['40pt'], text_color='red')
```

which i just tested to make sure works.


Resolving patch:
diff --git a/bokeh/plotting_helpers.py b/bokeh/plotting_helpers.py
--- a/bokeh/plotting_helpers.py
+++ b/bokeh/plotting_helpers.py
@@ -1,10 +1,11 @@
 from __future__ import absolute_import
 
 from collections import Iterable, OrderedDict, Sequence
+import difflib
 import itertools
-import numpy as np
 import re
-import difflib
+
+import numpy as np
 from six import string_types
 
 from .models import (
@@ -115,6 +116,12 @@ def _process_sequence_literals(glyphclass, kwargs, source):
     dataspecs = glyphclass.dataspecs_with_refs()
     for var, val in kwargs.items():
 
+        # ignore things that are not iterable
+        if not isinstance(val, Iterable): continue
+
+        # pass dicts (i.e., values or fields) on as-is
+        if isinstance(val, dict): continue
+
         # let any non-dataspecs do their own validation (e.g., line_dash properties)
         if var not in dataspecs: continue
 
@@ -125,14 +132,11 @@ def _process_sequence_literals(glyphclass, kwargs, source):
         if (isinstance(dataspecs[var], ColorSpec) and ColorSpec.is_color_tuple(val)):
             continue
 
-        if isinstance(val, np.ndarray):
-            if val.ndim != 1:
-                raise RuntimeError("Columns need to be 1D (%s is not)" % var)
-            source.add(val, name=var)
-            kwargs[var] = var
-        elif isinstance(val, Iterable):
-            source.add(val, name=var)
-            kwargs[var] = var
+        if isinstance(val, np.ndarray) and val.ndim != 1:
+            raise RuntimeError("Columns need to be 1D (%s is not)" % var)
+
+        source.add(val, name=var)
+        kwargs[var] = var
 
 def _make_glyph(glyphclass, kws, extra):
         kws = kws.copy()


Test patch: