I'm using pygal to chart some data in a web app I'm working on and thought it would be a good idea to externalize the configuration of the chart.
So I wrote a section in my conf file that replicated my in code conf:
[ChartOptions]
x_label_rotation: -75
x_labels_major_every: 5
show_minor_x_labels: False
range: (30,100)
stroke_style: {'width':8}
title: Chart Title
and found that passing the ChartOptions section to (for instance) pygal.Config() resulted in
File "/usr/local/lib/python2.7/dist-packages/pygal/util.py", line 370, in mergextend
if list1 is None or _ellipsis not in list1:
How can I do this?
I'm fairly new at Python, so maybe this is all unnecessary, is well known or there exists a better way. I had a bunch of trouble and couldn't find anything, so here we are.
The first thing I worked out is that pygal.util.mergextend() does not like to find strings where it's expecting other datatypes. The values in the OrderedDict returned from ConfigParser.read()._sections[your_section_here] are all strings, so they need to be converted to their proper type.
Enter: ast.literal_eval().
This seemed like it would work, but kept raising a ValueError('malformed string ') on the __name__
value, which was a str, per type(options['__name__
']). Well, now what?
I didn't really need the __name__
value, so I used pop()
to remove it from the dictionary, which left dealing with the title
value. I wanted to use title
, knew could be a string per pygal and had control over the value of it, so what can be done?
The docs for ast.literal_eval()
insist it allows strings, so adding quotes to the title
value, in the conf file seemed 'reasonable', and worked.
Putting all that together, and adding flask to the mix, we get:
conf file:
...
[ChartOptions]
x_label_rotation: -75
x_labels_major_every: 5
show_minor_x_labels: False
range: (30,100)
stroke_style: {'width':8}
# note added quotes below
title: "Chart Title"
...
app.py:
import ConfigParser
import ast
import pygal
from pygal import Config
from flask import Flask, render_template
...
config = ConfigParser.ConfigParser()
config.read('my.conf')
chart_options = config._sections[CHART_SECTION]
chart_options.pop('__name__')
for key in chart_options.keys():
chart_options[key] = ast.literal_eval(chart_options[key])
@app.route('/route')
def a_route():
global chart_options # haven't made this into a class yet...
chart_config = Config(**chart_options)
chart = pygal.Line(chart_config)
...
# add data and finalize chart setup
...
return render_template('route.html', chart=chart.render())
...