Browse Source

Add classes option. Handle ellipsis in list type configs to auto-extend parent.

pull/317/head
Florian Mounier 9 years ago
parent
commit
9ae5f3e6a5
  1. 7
      docs/changelog.rst
  2. 2
      pygal/__init__.py
  3. 7
      pygal/_compat.py
  4. 13
      pygal/config.py
  5. 5
      pygal/svg.py
  6. 24
      pygal/test/test_config.py
  7. 23
      pygal/test/test_util.py
  8. 9
      pygal/util.py

7
docs/changelog.rst

@ -2,6 +2,13 @@
Changelog Changelog
========= =========
2.2.2
=====
* Add classes option.
* Handle ellipsis in list type configs to auto-extend parent. (Viva python3)
2.2.0 2.2.0
===== =====

2
pygal/__init__.py

@ -24,7 +24,7 @@ and the maps extensions namespace module.
""" """
__version__ = '2.2.1' __version__ = '2.2.2'
import pkg_resources import pkg_resources
import sys import sys

7
pygal/_compat.py

@ -27,10 +27,17 @@ from datetime import datetime, timedelta, tzinfo
if sys.version_info[0] == 3: if sys.version_info[0] == 3:
base = (str, bytes) base = (str, bytes)
coerce = str coerce = str
_ellipsis = eval('...')
else: else:
base = basestring base = basestring
coerce = unicode coerce = unicode
class EllipsisGetter(object):
def __getitem__(self, key):
return key
_ellipsis = EllipsisGetter()[...]
def is_list_like(value): def is_list_like(value):
"""Return whether value is an iterable but not a mapping / string""" """Return whether value is an iterable but not a mapping / string"""

13
pygal/config.py

@ -22,6 +22,7 @@ from copy import deepcopy
from pygal.interpolate import INTERPOLATIONS from pygal.interpolate import INTERPOLATIONS
from pygal.style import DefaultStyle, Style from pygal.style import DefaultStyle, Style
from pygal.util import mergextend
from pygal import formatters from pygal import formatters
@ -170,8 +171,11 @@ class BaseConfig(MetaConfig('ConfigBase', (object,), {})):
"""Update the config with the given dictionary""" """Update the config with the given dictionary"""
dir_self_set = set(dir(self)) dir_self_set = set(dir(self))
self.__dict__.update( self.__dict__.update(
dict([(k, v) for (k, v) in kwargs.items() dict([
if not k.startswith('_') and k in dir_self_set])) (k, mergextend(v, self.__dict__.get(k, ())))
if getattr(Config, k, Key(*[''] * 4)).type == list
else (k, v) for (k, v) in kwargs.items()
if not k.startswith('_') and k in dir_self_set]))
def to_dict(self): def to_dict(self):
"""Export a JSON serializable dictionary of the config""" """Export a JSON serializable dictionary of the config"""
@ -244,6 +248,11 @@ class Config(CommonConfig):
"It can be any uri from file:///tmp/style.css to //domain/style.css", "It can be any uri from file:///tmp/style.css to //domain/style.css",
str) str)
classes = Key(
('pygal-chart',),
list, "Style", "Classes of the root svg node",
str)
defs = Key( defs = Key(
[], [],
list, "Misc", "Extraneous defs to be inserted in svg", list, "Misc", "Extraneous defs to be inserted in svg",

5
pygal/svg.py

@ -68,10 +68,11 @@ class Svg(object):
self.root = etree.Element('svg', **attrs) self.root = etree.Element('svg', **attrs)
self.root.attrib['id'] = self.id.lstrip('#').rstrip() self.root.attrib['id'] = self.id.lstrip('#').rstrip()
self.root.attrib['class'] = 'pygal-chart' if graph.classes:
self.root.attrib['class'] = ' '.join(graph.classes)
self.root.append( self.root.append(
etree.Comment(u( etree.Comment(u(
'Generated with pygal %s (%s) ©Kozea 2011-2015 on %s' % ( 'Generated with pygal %s (%s) ©Kozea 2011-2016 on %s' % (
__version__, __version__,
'lxml' if etree.lxml else 'etree', 'lxml' if etree.lxml else 'etree',
date.today().isoformat())))) date.today().isoformat()))))

24
pygal/test/test_config.py

@ -542,3 +542,27 @@ def test_formatters(Chart):
assert set([v.text for v in q(".value")]) == set(( assert set([v.text for v in q(".value")]) == set((
u('4€'), u('5€'), u('6€'), '1_a$', '2_a$', u('3¥')) + ( u('4€'), u('5€'), u('6€'), '1_a$', '2_a$', u('3¥')) + (
('6_a$', u('15€')) if Chart in (Pie, SolidGauge) else ())) ('6_a$', u('15€')) if Chart in (Pie, SolidGauge) else ()))
def test_classes(Chart):
"""Test classes option"""
chart = Chart()
assert chart.render_pyquery().attr('class') == 'pygal-chart'
chart = Chart(classes=())
assert not chart.render_pyquery().attr('class')
chart = Chart(classes=(...,))
assert chart.render_pyquery().attr('class') == 'pygal-chart'
chart = Chart(classes=('graph',))
assert chart.render_pyquery().attr('class') == 'graph'
chart = Chart(classes=('pygal-chart', 'graph'))
assert chart.render_pyquery().attr('class') == 'pygal-chart graph'
chart = Chart(classes=(..., 'graph'))
assert chart.render_pyquery().attr('class') == 'pygal-chart graph'
chart = Chart(classes=('graph', ...))
assert chart.render_pyquery().attr('class') == 'graph pygal-chart'

23
pygal/test/test_util.py

@ -19,11 +19,12 @@
"""Utility functions tests""" """Utility functions tests"""
from pygal._compat import u from pygal._compat import u, _ellipsis
from pygal.util import ( from pygal.util import (
round_to_int, round_to_float, _swap_curly, template, round_to_int, round_to_float, _swap_curly, template,
truncate, minify_css, majorize) truncate, minify_css, majorize, mergextend)
from pytest import raises from pytest import raises
import sys
def test_round_to_int(): def test_round_to_int():
@ -153,3 +154,21 @@ def test_majorize():
assert majorize(range(21, 83, 3)) == [30, 45, 60, 75] assert majorize(range(21, 83, 3)) == [30, 45, 60, 75]
# TODO: handle crazy cases # TODO: handle crazy cases
# assert majorize(range(20, 83, 3)) == [20, 35, 50, 65, 80] # assert majorize(range(20, 83, 3)) == [20, 35, 50, 65, 80]
def test_mergextend():
"""Test mergextend function"""
assert mergextend(['a', 'b'], ['c', 'd']) == ['a', 'b']
assert mergextend([], ['c', 'd']) == []
assert mergextend(['a', 'b'], []) == ['a', 'b']
assert mergextend([_ellipsis], ['c', 'd']) == ['c', 'd']
assert mergextend([_ellipsis, 'b'], ['c', 'd']) == ['c', 'd', 'b']
assert mergextend(['a', _ellipsis], ['c', 'd']) == ['a', 'c', 'd']
assert mergextend(['a', _ellipsis, 'b'], ['c', 'd']) == [
'a', 'c', 'd', 'b']
if sys.version_info[0] >= 3:
# For @#! sake it's 2016 now
assert eval("mergextend(['a', ..., 'b'], ['c', 'd'])") == [
'a', 'c', 'd', 'b']

9
pygal/util.py

@ -26,7 +26,7 @@ from decimal import Decimal
from math import ceil, floor, log10, pi, cos, sin from math import ceil, floor, log10, pi, cos, sin
from pygal._compat import to_unicode, u from pygal._compat import to_unicode, u, _ellipsis
def float_format(number): def float_format(number):
@ -364,3 +364,10 @@ def coord_dual(r):
def coord_abs_project(center, rho, theta): def coord_abs_project(center, rho, theta):
return coord_format(coord_diff(center, coord_project(rho, theta))) return coord_format(coord_diff(center, coord_project(rho, theta)))
def mergextend(list1, list2):
if _ellipsis not in list1:
return list1
index = list1.index(_ellipsis)
return list(list1[:index]) + list(list2) + list(list1[index + 1:])

Loading…
Cancel
Save