diff --git a/.pylintrc b/.pylintrc index 940c3e7..242f3c7 100644 --- a/.pylintrc +++ b/.pylintrc @@ -33,7 +33,7 @@ load-plugins= # can either give multiple identifier separated by comma (,) or put this option # multiple time (only on the command line, not in the configuration file where # it should appear only once). -disable=W0142 +disable=I0011,W0142,C0102 [REPORTS] @@ -133,7 +133,7 @@ module-rgx=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$ const-rgx=(([A-Za-z_][A-Za-z0-9_]*)|(__.*__))$ # Regular expression which should only match correct class names -class-rgx=[A-Z_][a-zA-Z0-9]+$ +class-rgx=[a-zA-Z_][a-zA-Z0-9]+$ # Regular expression which should only match correct function names function-rgx=[a-z_][a-z0-9_]{2,30}$ @@ -148,7 +148,7 @@ attr-rgx=[a-z_][a-z0-9_]{0,30}$ argument-rgx=[a-z_][a-z0-9_]{0,30}$ # Regular expression which should only match correct variable names -variable-rgx=[a-z_][a-z0-9_]{0,30}$ +variable-rgx=[A-Za-z_][a-z0-9_]{0,30}$ # Regular expression which should only match correct list comprehension / # generator expression variable names diff --git a/pygal/graph/base.py b/pygal/graph/base.py index 855d48f..7e9e64e 100644 --- a/pygal/graph/base.py +++ b/pygal/graph/base.py @@ -42,8 +42,8 @@ class BaseGraph(object): """Add a serie to this graph""" self.series.append(Serie(title, values, len(self.series))) - def _init(self): - """Init the graph""" + def reinit(self): + """(Re-)Init the graph""" self.margin = Margin(*([20] * 4)) self._box = Box() @@ -191,13 +191,13 @@ class BaseGraph(object): def _render(self): """Make the graph internally""" - self._init() - self.svg._init() + self.reinit() + self.svg.reinit() if self._has_data(): self._draw() - self.svg._pre_render(False) + self.svg.pre_render(False) else: - self.svg._pre_render(True) + self.svg.pre_render(True) def render(self, is_unicode=False): """Render the graph, and return the svg string""" diff --git a/pygal/svg.py b/pygal/svg.py index 8a49f93..64c1a46 100644 --- a/pygal/svg.py +++ b/pygal/svg.py @@ -16,11 +16,16 @@ # # You should have received a copy of the GNU Lesser General Public License # along with pygal. If not, see . +""" +Svg helper + +""" + from __future__ import division import io import os from lxml import etree -from pygal.util import template +from pygal.util import template, coord_format from pygal import __version__ @@ -30,8 +35,11 @@ class Svg(object): def __init__(self, graph): self.graph = graph + self.root = None + self.defs = None - def _init(self): + def reinit(self): + """(Re-)initialization""" self.root = etree.Element( "{%s}svg" % self.ns, nsmap={ @@ -45,6 +53,7 @@ class Svg(object): self.defs = self.node(tag='defs') def add_style(self, css): + """Add the css to the svg""" style = self.node(self.defs, 'style', type='text/css') with io.open(css, encoding='utf-8') as f: templ = template( @@ -55,6 +64,7 @@ class Svg(object): style.text = templ def add_script(self, js): + """Add the js to the svg""" script = self.node(self.defs, 'script', type='text/javascript') with io.open(js, encoding='utf-8') as f: templ = template( @@ -65,6 +75,7 @@ class Svg(object): script.text = templ def node(self, parent=None, tag='g', attrib=None, **extras): + """Make a new svg node""" if parent is None: parent = self.root attrib = attrib or {} @@ -80,6 +91,7 @@ class Svg(object): return etree.SubElement(parent, tag, attrib) def transposable_node(self, parent=None, tag='g', attrib=None, **extras): + """Make a new svg node which can be transposed if horizontal""" if self.graph._horizontal: for key1, key2 in (('x', 'y'), ('width', 'height')): attr1 = extras.get(key1, None) @@ -87,24 +99,23 @@ class Svg(object): extras[key1], extras[key2] = attr2, attr1 return self.node(parent, tag, attrib, **extras) - def format(self, xy): - return '%f %f' % xy - def line(self, node, coords, close=False, **kwargs): + """Draw a svg line""" if len(coords) < 2: return root = 'M%s L%s Z' if close else 'M%s L%s' origin_index = 0 while None in coords[origin_index]: origin_index += 1 - origin = self.format(coords[origin_index]) - line = ' '.join([self.format(c) + origin = coord_format(coords[origin_index]) + line = ' '.join([coord_format(c) for c in coords[origin_index + 1:] if None not in c]) self.node(node, 'path', d=root % (origin, line), **kwargs) - def _pre_render(self, no_data=False): + def pre_render(self, no_data=False): + """Last things to do before rendering""" self.add_style(self.graph.base_css or os.path.join( os.path.dirname(__file__), 'css', 'graph.css')) self.add_script(self.graph.base_js or os.path.join( @@ -122,6 +133,7 @@ class Svg(object): no_data.text = self.graph.no_data_text def render(self, is_unicode=False): + """Last thing to do before rendering""" svg = etree.tostring( self.root, pretty_print=True, xml_declaration=not self.graph.disable_xml_declaration, diff --git a/pygal/util.py b/pygal/util.py index 1a9ad3f..a1e2793 100644 --- a/pygal/util.py +++ b/pygal/util.py @@ -16,6 +16,11 @@ # # You should have received a copy of the GNU Lesser General Public License # along with pygal. If not, see . +""" +Various utils + +""" + from __future__ import division from decimal import Decimal from math import floor, pi, log, log10 @@ -23,10 +28,12 @@ ORDERS = u"yzafpnµm kMGTPEZY" def float_format(number): + """Format a float to a precision of 3, without zeroes or dots""" return ("%.3f" % number).rstrip('0').rstrip('.') def humanize(number): + """Format a number to engineer scale""" order = number and int(floor(log(abs(number)) / log(1000))) human_readable = ORDERS.split(" ")[int(order > 0)] if order == 0 or order > len(human_readable): @@ -42,37 +49,43 @@ def is_major(number): def round_to_int(number, precision): + """Round a number to a precision""" precision = int(precision) rounded = (int(number) + precision / 2) // precision * precision return rounded def round_to_float(number, precision): + """Round a float to a precision""" rounded = Decimal(str(floor((number + precision / 2) // precision)) ) * Decimal(str(precision)) return float(rounded) def round_to_scale(number, precision): + """Round a number or a float to a precision""" if precision < 1: return round_to_float(number, precision) return round_to_int(number, precision) def cut(list_, index=0): + """Cut a list by index or arg""" if isinstance(index, int): - cut = lambda x: x[index] + cut_ = lambda x: x[index] else: - cut = lambda x: getattr(x, index) - return map(cut, list_) + cut_ = lambda x: getattr(x, index) + return map(cut_, list_) -def rad(deg): - return pi * deg / 180 +def rad(degrees): + """Convert degrees in radiants""" + return pi * degrees / 180 -def deg(deg): - return 180 * deg / pi +def deg(radiants): + """Convert radiants in degrees""" + return 180 * radiants / pi def _swap_curly(string): @@ -92,19 +105,25 @@ def template(string, **kwargs): """Format a string using double braces""" return _swap_curly(string).format(**kwargs) + +def coord_format(xy): + """Format x y coords to svg""" + return '%f %f' % xy + swap = lambda tuple_: tuple(reversed(tuple_)) ident = lambda x: x # Stolen from brownie http://packages.python.org/Brownie/ class cached_property(object): + """Optimize a static property""" def __init__(self, getter, doc=None): self.getter = getter self.__module__ = getter.__module__ self.__name__ = getter.__name__ self.__doc__ = doc or getter.__doc__ - def __get__(self, obj, type=None): + def __get__(self, obj, type_=None): if obj is None: return self value = obj.__dict__[self.__name__] = self.getter(obj) diff --git a/pygal/view.py b/pygal/view.py index 66bdc9b..9e865ec 100644 --- a/pygal/view.py +++ b/pygal/view.py @@ -136,6 +136,7 @@ class LogView(View): self.log10_ymin = log10(self.box.ymin) self.box.fix(False) + # pylint: enable-msg=W0231 def y(self, y): """Project y""" if y == None or y <= 0: