diff --git a/pygal/__init__.py b/pygal/__init__.py
index 87121f4..f9ad508 100644
--- a/pygal/__init__.py
+++ b/pygal/__init__.py
@@ -17,7 +17,7 @@
# You should have received a copy of the GNU Lesser General Public License
# along with pygal. If not, see .
"""
-Main pygal package
+Main pygal package.
This package holds all available charts in pygal, the Config class
and the maps extensions namespace module.
@@ -76,16 +76,17 @@ CHARTS = list(CHARTS_BY_NAME.values())
class PluginImportFixer(object):
- """Allow external map plugins to be imported from pygal.maps package.
- It is a ``sys.meta_path`` loader.
+
"""
+ Allow external map plugins to be imported from pygal.maps package.
- def __init__(self):
- pass
+ It is a ``sys.meta_path`` loader.
+ """
def find_module(self, fullname, path=None):
- """This method says if the module to load can be loaded by
- the load_module function, that is here if it is a ``pygal.maps.*``
+ """
+ Tell if the module to load can be loaded by
+ the load_module function, ie: if it is a ``pygal.maps.*``
module.
"""
if fullname.startswith('pygal.maps.') and hasattr(
@@ -94,8 +95,10 @@ class PluginImportFixer(object):
return None
def load_module(self, name):
- """Loads the ``pygal.maps.name`` module from
- the previously loaded plugin"""
+ """
+ Load the ``pygal.maps.name`` module from the previously
+ loaded plugin
+ """
if name not in sys.modules:
sys.modules[name] = getattr(maps, name.split('.')[2])
return sys.modules[name]
diff --git a/pygal/_compat.py b/pygal/_compat.py
index 2dc9e08..d5ce470 100644
--- a/pygal/_compat.py
+++ b/pygal/_compat.py
@@ -16,9 +16,7 @@
#
# You should have received a copy of the GNU Lesser General Public License
# along with pygal. If not, see .
-"""
-Various hacks for transparent python 2 / python 3 support
-"""
+"""Various hacks for transparent python 2 / python 3 support"""
import sys
from collections import Iterable
@@ -34,32 +32,38 @@ else:
def is_list_like(value):
+ """Return whether value is an iterable but not a mapping / string"""
return isinstance(value, Iterable) and not isinstance(value, (base, dict))
def is_str(string):
+ """Return whether value is a string or a byte list"""
return isinstance(string, base)
-def to_str(string):
- if not is_str(string):
- return coerce(string)
- return string
+def to_str(obj):
+ """Cast obj to unicode string"""
+ if not is_str(obj):
+ return coerce(obj)
+ return obj
def to_unicode(string):
+ """Force stirng to be a string in python 3 or a unicode in python 2"""
if not isinstance(string, coerce):
return string.decode('utf-8')
return string
def u(s):
+ """Emulate u'str' in python 2, do nothing in python 3"""
if sys.version_info[0] == 2:
return s.decode('utf-8')
return s
def total_seconds(td):
+ """Backport of `timedelta.total_second` function for python 2.6"""
if sys.version_info[:2] == (2, 6):
return (
(td.days * 86400 + td.seconds) * 10 ** 6 + td.microseconds
@@ -68,6 +72,7 @@ def total_seconds(td):
def timestamp(x):
+ """Get a timestamp from a date in python 3 and python 2"""
if hasattr(x, 'timestamp'):
from datetime import timezone
if x.tzinfo is None:
diff --git a/pygal/adapters.py b/pygal/adapters.py
index 95ef55a..3720c63 100644
--- a/pygal/adapters.py
+++ b/pygal/adapters.py
@@ -16,10 +16,7 @@
#
# You should have received a copy of the GNU Lesser General Public License
# along with pygal. If not, see .
-"""
-Value adapters to use when a chart doesn't accept all value types
-
-"""
+"""Value adapters to use when a chart doesn't accept all value types"""
from decimal import Decimal
diff --git a/pygal/colors.py b/pygal/colors.py
index fa47e37..9f6df7a 100644
--- a/pygal/colors.py
+++ b/pygal/colors.py
@@ -119,9 +119,10 @@ def parse_color(color):
def unparse_color(r, g, b, a, type):
- """Take the r, g, b, a color values and give back
- a type css color string. This is the inverse function of parse_color"""
-
+ """
+ Take the r, g, b, a color values and give back
+ a type css color string. This is the inverse function of parse_color
+ """
if type == '#rgb':
# Don't lose precision on rgb shortcut
if r % 17 == 0 and g % 17 == 0 and b % 17 == 0:
@@ -168,7 +169,7 @@ def adjust(color, attribute, percent):
def rotate(color, percent):
- """Rotates a color by changing its hue value by percent"""
+ """Rotate a color by changing its hue value by percent"""
return adjust(color, 0, percent)
diff --git a/pygal/config.py b/pygal/config.py
index bf0a7db..87a86b8 100644
--- a/pygal/config.py
+++ b/pygal/config.py
@@ -16,10 +16,8 @@
#
# You should have received a copy of the GNU Lesser General Public License
# along with pygal. If not, see .
+"""Config module holding all options and their default values."""
-"""
-Config module holding all options and their default values.
-"""
from copy import deepcopy
from pygal.style import Style, DefaultStyle
from pygal.interpolate import INTERPOLATIONS
@@ -29,6 +27,7 @@ CONFIG_ITEMS = []
class Key(object):
+
"""
Represents a config parameter.
@@ -45,7 +44,7 @@ class Key(object):
def __init__(
self, default_value, type_, category, doc,
subdoc="", subtype=None):
-
+ """Create a configuration key"""
self.value = default_value
self.type = type_
self.doc = doc
@@ -59,6 +58,10 @@ class Key(object):
CONFIG_ITEMS.append(self)
def __repr__(self):
+ """
+ Make a documentation repr.
+ This is a hack to generate doc from inner doc
+ """
return """
Type: %s%s
Default: %r
@@ -120,10 +123,11 @@ class Key(object):
class MetaConfig(type):
- """
- Metaclass for configs. Used to get the key name and set it on the value.
- """
+
+ """Config metaclass. Used to get the key name and set it on the value."""
+
def __new__(mcs, classname, bases, classdict):
+ """Get the name of the key and set it on the key"""
for k, v in classdict.items():
if isinstance(v, Key):
v.name = k
@@ -132,6 +136,7 @@ class MetaConfig(type):
class BaseConfig(MetaConfig('ConfigBase', (object,), {})):
+
"""
This class holds the common method for configs.
@@ -159,7 +164,7 @@ class BaseConfig(MetaConfig('ConfigBase', (object,), {})):
self._update(kwargs)
def _update(self, kwargs):
- """Updates the config with the given dictionary"""
+ """Update the config with the given dictionary"""
self.__dict__.update(
dict([(k, v) for (k, v) in kwargs.items()
if not k.startswith('_') and k in dir(self)]))
@@ -182,6 +187,7 @@ class BaseConfig(MetaConfig('ConfigBase', (object,), {})):
class CommonConfig(BaseConfig):
+
"""Class holding options used in both chart and serie configuration"""
stroke = Key(
@@ -213,6 +219,7 @@ class CommonConfig(BaseConfig):
class Config(CommonConfig):
+
"""Class holding config values"""
style = Key(
@@ -496,6 +503,7 @@ class Config(CommonConfig):
class SerieConfig(CommonConfig):
+
"""Class holding serie config values"""
secondary = Key(
diff --git a/pygal/etree.py b/pygal/etree.py
index aa5a46f..721a0a7 100644
--- a/pygal/etree.py
+++ b/pygal/etree.py
@@ -25,9 +25,11 @@ import os
class Etree(object):
+
"""Etree wrapper using lxml.etree or standard xml.etree"""
def __init__(self):
+ """Create the wrapper"""
from xml.etree import ElementTree as _py_etree
self._py_etree = _py_etree
try:
@@ -43,6 +45,7 @@ class Etree(object):
self.lxml = self._etree is self._lxml_etree
def __getattribute__(self, attr):
+ """Retrieve attr from current active etree implementation"""
if (attr not in object.__getattribute__(self, '__dict__') and
attr not in Etree.__dict__):
return object.__getattribute__(self._etree, attr)
diff --git a/pygal/interpolate.py b/pygal/interpolate.py
index 975a6aa..241f0b9 100644
--- a/pygal/interpolate.py
+++ b/pygal/interpolate.py
@@ -33,7 +33,6 @@ def quadratic_interpolate(x, y, precision=250, **kwargs):
Interpolate x, y using a quadratic algorithm
https://en.wikipedia.org/wiki/Spline_(mathematics)
"""
-
n = len(x) - 1
delta_x = [x2 - x1 for x1, x2 in zip(x, x[1:])]
delta_y = [y2 - y1 for y1, y2 in zip(y, y[1:])]
diff --git a/pygal/serie.py b/pygal/serie.py
index 156b46d..e5b105c 100644
--- a/pygal/serie.py
+++ b/pygal/serie.py
@@ -16,16 +16,16 @@
#
# You should have received a copy of the GNU Lesser General Public License
# along with pygal. If not, see .
-"""
-Little helpers for series
-
-"""
+"""Serie property holder"""
from pygal.util import cached_property
class Serie(object):
- """Serie containing title, values and the graph serie index"""
+
+ """Serie class containing title, values and the graph serie index"""
+
def __init__(self, index, title, values, config, metadata=None):
+ """Create the serie with its options"""
self.index = index
self.title = title
self.values = values
@@ -35,4 +35,5 @@ class Serie(object):
@cached_property
def safe_values(self):
+ """Property containing all values that are not None"""
return list(filter(lambda x: x is not None, self.values))
diff --git a/pygal/state.py b/pygal/state.py
index eafcc7a..96ce666 100644
--- a/pygal/state.py
+++ b/pygal/state.py
@@ -16,14 +16,20 @@
#
# You should have received a copy of the GNU Lesser General Public License
# along with pygal. If not, see .
-"""
-Class holding state during render
-"""
+"""Class holding state during render"""
class State(object):
+
+ """
+ Class containing config values
+ overriden by chart values
+ overriden by keyword args
+ """
+
def __init__(self, graph, **kwargs):
+ """Create the transient state"""
self.__dict__.update(**graph.config.__dict__)
self.__dict__.update(**graph.__dict__)
self.__dict__.update(**kwargs)
diff --git a/pygal/style.py b/pygal/style.py
index 0d33641..102da10 100644
--- a/pygal/style.py
+++ b/pygal/style.py
@@ -16,9 +16,8 @@
#
# You should have received a copy of the GNU Lesser General Public License
# along with pygal. If not, see .
-"""
-Charts styling
-"""
+"""Charts styling classes"""
+
from __future__ import division
from pygal.util import cycle_fill
from pygal import colors
@@ -27,7 +26,9 @@ import sys
class Style(object):
+
"""Styling class containing colors for the css generation"""
+
def __init__(
self,
background='black',
@@ -45,6 +46,7 @@ class Style(object):
'#899ca1', '#f8f8f2', '#bf4646', '#516083', '#f92672',
'#82b414', '#fd971f', '#56c2d6', '#808384', '#8c54fe',
'#465457')):
+ """Create the style"""
self.background = background
self.plot_background = plot_background
self.foreground = foreground
@@ -58,7 +60,6 @@ class Style(object):
def get_colors(self, prefix, len_):
"""Get the css color list"""
-
def color(tupl):
"""Make a color css"""
return ((
@@ -75,6 +76,7 @@ class Style(object):
return '\n'.join(map(color, enumerate(colors)))
def to_dict(self):
+ """Convert instance to a serializable mapping."""
config = {}
for attr in dir(self):
if not attr.startswith('__'):
@@ -296,10 +298,23 @@ for op in ('lighten', 'darken', 'saturate', 'desaturate', 'rotate'):
name = op.capitalize() + 'Style'
def get_style_for(op_name):
+ """
+ Return a callable that returns a Style instance
+ for the given operation
+ """
operation = getattr(colors, op_name)
def parametric_style(color, step=10, max_=None, base_style=None,
**kwargs):
+ """
+ Generate a parametric Style instance of the parametric operation
+ This takes several parameters:
+ * a `step` which correspond on how many colors will be needed
+ * a `max_` which defines the maximum amplitude of the color effect
+ * a `base_style` which will be taken as default for everything
+ except colors
+ * any keyword arguments setting other style parameters
+ """
if max_ is None:
violency = {
'darken': 50,
diff --git a/pygal/svg.py b/pygal/svg.py
index b65443b..06c3bd8 100644
--- a/pygal/svg.py
+++ b/pygal/svg.py
@@ -16,10 +16,8 @@
#
# You should have received a copy of the GNU Lesser General Public License
# along with pygal. If not, see .
-"""
-Svg helper
-"""
+"""Svg helper"""
from __future__ import division
from pygal._compat import to_str, u
@@ -35,11 +33,14 @@ from pygal import __version__
class Svg(object):
- """Svg object"""
+
+ """Svg related methods"""
+
ns = 'http://www.w3.org/2000/svg'
xlink_ns = 'http://www.w3.org/1999/xlink'
def __init__(self, graph):
+ """Create the svg helper with the chart instance"""
self.graph = graph
if not graph.no_prefix:
self.id = '#chart-%s ' % graph.uuid
@@ -98,7 +99,9 @@ class Svg(object):
os.path.dirname(__file__), 'css', css)
class FontSizes(object):
+
"""Container for font sizes"""
+
fs = FontSizes()
for name in dir(self.graph.state):
if name.endswith('_font_size'):
@@ -286,6 +289,7 @@ class Svg(object):
self.root.set('height', str(self.graph.height))
def draw_no_data(self):
+ """Write the no data text to the svg"""
no_data = self.node(self.graph.nodes['text_overlay'], 'text',
x=self.graph.view.width / 2,
y=self.graph.view.height / 2,
@@ -314,7 +318,9 @@ class Svg(object):
return svg
def get_strokes(self):
+ """Return a css snippet containing all stroke style options"""
def stroke_dict_to_css(stroke, i=None):
+ """Return a css style for the given option"""
css = ['%s.series%s {\n' % (
self.id, '.serie-%d' % i if i is not None else '')]
for key in ('width', 'linejoin', 'linecap',
diff --git a/pygal/table.py b/pygal/table.py
index ed9a05e..c32168d 100644
--- a/pygal/table.py
+++ b/pygal/table.py
@@ -17,8 +17,9 @@
# You should have received a copy of the GNU Lesser General Public License
# along with pygal. If not, see .
"""
-Table maker
+HTML Table maker.
+This class is used to render an html table from a chart data.
"""
from pygal.util import template
@@ -27,18 +28,32 @@ import uuid
class HTML(object):
+
+ """Lower case adapter of lxml builder"""
+
def __getattribute__(self, attr):
+ """Get the uppercase builder attribute"""
return getattr(builder, attr.upper())
class Table(object):
+
+ """Table generator class"""
+
_dual = None
def __init__(self, chart):
- "Init the table"
+ """Init the table"""
self.chart = chart
def render(self, total=False, transpose=False, style=False):
+ """Render the HTMTL table of the chart.
+
+ `total` can be specified to include data sums
+ `transpose` make labels becomes columns
+ `style` include scoped style for the table
+
+ """
self.chart.setup()
ln = self.chart._len
fmt = self.chart._format
diff --git a/pygal/util.py b/pygal/util.py
index 94f749f..1fa96f0 100644
--- a/pygal/util.py
+++ b/pygal/util.py
@@ -16,10 +16,9 @@
#
# You should have received a copy of the GNU Lesser General Public License
# along with pygal. If not, see .
-"""
-Various utils
-"""
+"""Various utility functions"""
+
from __future__ import division
from pygal._compat import u, is_list_like, to_unicode
import re
@@ -250,6 +249,7 @@ def decorate(svg, node, metadata):
def alter(node, metadata):
+ """Override nodes attributes from metadata node mapping"""
if node is not None and metadata and 'node' in metadata:
node.attrib.update(
dict((k, str(v)) for k, v in metadata['node'].items()))
@@ -273,14 +273,21 @@ def truncate(string, index):
# # Stolen partly from brownie http://packages.python.org/Brownie/
class cached_property(object):
- """Optimize a static property"""
+
+ """Memoize a property"""
+
def __init__(self, getter, doc=None):
+ """Initialize the decorator"""
self.getter = getter
self.__module__ = getter.__module__
self.__name__ = getter.__name__
self.__doc__ = doc or getter.__doc__
def __get__(self, obj, type_=None):
+ """
+ Get descriptor calling the property function and replacing it with
+ its value or on state if we are in the transient state.
+ """
if obj is None:
return self
value = self.getter(obj)
@@ -294,6 +301,7 @@ css_comments = re.compile(r'/\*.*?\*/', re.MULTILINE | re.DOTALL)
def minify_css(css):
+ """Little css minifier"""
# Inspired by slimmer by Peter Bengtsson
remove_next_comment = 1
for css_comment in css_comments.findall(css):
@@ -328,12 +336,14 @@ def compose(f, g):
def safe_enumerate(iterable):
+ """Enumerate which does not yield None values"""
for i, v in enumerate(iterable):
if v is not None:
yield i, v
def split_title(title, width, title_fs):
+ """Split a string for a specified width and font size"""
titles = []
if not title:
return titles
diff --git a/pygal/view.py b/pygal/view.py
index 07f5e28..a1c74b8 100644
--- a/pygal/view.py
+++ b/pygal/view.py
@@ -16,17 +16,19 @@
#
# You should have received a copy of the GNU Lesser General Public License
# along with pygal. If not, see .
-"""
-Projection and bounding helpers
-"""
+
+"""Projection and bounding helpers"""
from __future__ import division
from math import sin, cos, log10, pi
class Margin(object):
- """Graph margin"""
+
+ """Class reprensenting a margin (top, right, left, bottom)"""
+
def __init__(self, top, right, bottom, left):
+ """Create the margin object from the top, right, left, bottom margin"""
self.top = top
self.right = right
self.bottom = bottom
@@ -44,16 +46,23 @@ class Margin(object):
class Box(object):
+
"""Chart boundings"""
+
margin = .02
def __init__(self, xmin=0, ymin=0, xmax=1, ymax=1):
+ """
+ Create the chart bounds with min max horizontal
+ and vertical values
+ """
self._xmin = xmin
self._ymin = ymin
self._xmax = xmax
self._ymax = ymax
def set_polar_box(self, rmin=0, rmax=1, tmin=0, tmax=2 * pi):
+ """Helper for polar charts"""
self._rmin = rmin
self._rmax = rmax
self._tmin = tmin
@@ -63,37 +72,45 @@ class Box(object):
@property
def xmin(self):
+ """X minimum getter"""
return self._xmin
@xmin.setter
def xmin(self, value):
+ """X minimum setter"""
if value:
self._xmin = value
@property
def ymin(self):
+ """Y minimum getter"""
return self._ymin
@ymin.setter
def ymin(self, value):
+ """Y minimum setter"""
if value:
self._ymin = value
@property
def xmax(self):
+ """X maximum getter"""
return self._xmax
@xmax.setter
def xmax(self, value):
+ """X maximum setter"""
if value:
self._xmax = value
@property
def ymax(self):
+ """Y maximum getter"""
return self._ymax
@ymax.setter
def ymax(self, value):
+ """Y maximum setter"""
if value:
self._ymax = value
@@ -129,8 +146,11 @@ class Box(object):
class View(object):
+
"""Projection base class"""
+
def __init__(self, width, height, box):
+ """Create the view with a width an height and a box bounds"""
self.width = width
self.height = height
self.box = box
@@ -156,14 +176,22 @@ class View(object):
class ReverseView(View):
+
+ """Same as view but reversed vertically"""
+
def y(self, y):
+ """Project reversed y"""
if y is None:
return None
return (self.height * (y - self.box.ymin) / self.box.height)
class HorizontalView(View):
+
+ """Same as view but transposed"""
+
def __init__(self, width, height, box):
+ """Create the view with a width an height and a box bounds"""
self._force_vertical = None
self.width = width
self.height = height
@@ -173,7 +201,7 @@ class HorizontalView(View):
self.box.swap()
def x(self, x):
- """Project x"""
+ """Project x as y"""
if x is None:
return None
if self._force_vertical:
@@ -181,6 +209,7 @@ class HorizontalView(View):
return super(HorizontalView, self).y(x)
def y(self, y):
+ """Project y as x"""
if y is None:
return None
if self._force_vertical:
@@ -189,6 +218,7 @@ class HorizontalView(View):
class PolarView(View):
+
"""Polar projection for pie like graphs"""
def __call__(self, rhotheta):
@@ -201,9 +231,11 @@ class PolarView(View):
class PolarLogView(View):
+
"""Logarithmic polar projection"""
def __init__(self, width, height, box):
+ """Create the view with a width an height and a box bounds"""
super(PolarLogView, self).__init__(width, height, box)
if not hasattr(box, '_rmin') or not hasattr(box, '_rmax'):
raise Exception(
@@ -230,9 +262,11 @@ class PolarLogView(View):
class PolarThetaView(View):
+
"""Logarithmic polar projection"""
def __init__(self, width, height, box, aperture=pi / 3):
+ """Create the view with a width an height and a box bounds"""
super(PolarThetaView, self).__init__(width, height, box)
self.aperture = aperture
if not hasattr(box, '_tmin') or not hasattr(box, '_tmax'):
@@ -253,9 +287,11 @@ class PolarThetaView(View):
class PolarThetaLogView(View):
+
"""Logarithmic polar projection"""
def __init__(self, width, height, box, aperture=pi / 3):
+ """Create the view with a width an height and a box bounds"""
super(PolarThetaLogView, self).__init__(width, height, box)
self.aperture = aperture
if not hasattr(box, '_tmin') or not hasattr(box, '_tmax'):
@@ -288,9 +324,12 @@ class PolarThetaLogView(View):
class LogView(View):
- """Logarithmic projection """
+
+ """Y Logarithmic projection"""
+
# Do not want to call the parent here
def __init__(self, width, height, box):
+ """Create the view with a width an height and a box bounds"""
self.width = width
self.height = height
self.box = box
@@ -310,9 +349,12 @@ class LogView(View):
class XLogView(View):
- """Logarithmic projection """
+
+ """X logarithmic projection"""
+
# Do not want to call the parent here
def __init__(self, width, height, box):
+ """Create the view with a width an height and a box bounds"""
self.width = width
self.height = height
self.box = box
@@ -330,7 +372,11 @@ class XLogView(View):
class XYLogView(XLogView, LogView):
+
+ """X and Y logarithmic projection"""
+
def __init__(self, width, height, box):
+ """Create the view with a width an height and a box bounds"""
self.width = width
self.height = height
self.box = box
@@ -342,9 +388,12 @@ class XYLogView(XLogView, LogView):
class HorizontalLogView(XLogView):
- """Logarithmic projection """
+
+ """Transposed Logarithmic projection"""
+
# Do not want to call the parent here
def __init__(self, width, height, box):
+ """Create the view with a width an height and a box bounds"""
self._force_vertical = None
self.width = width
self.height = height
@@ -357,7 +406,7 @@ class HorizontalLogView(XLogView):
self.box.swap()
def x(self, x):
- """Project x"""
+ """Project x as y"""
if x is None:
return None
if self._force_vertical:
@@ -365,6 +414,7 @@ class HorizontalLogView(XLogView):
return super(XLogView, self).y(x)
def y(self, y):
+ """Project y as x"""
if y is None:
return None
if self._force_vertical: