diff --git a/features/steps/testing.py b/features/steps/testing.py index f0e70f55..ae338cca 100644 --- a/features/steps/testing.py +++ b/features/steps/testing.py @@ -20,6 +20,8 @@ import toyplot.require import toyplot.svg +from toyplot.require import as_float + try: import toyplot.pdf except: @@ -146,7 +148,7 @@ def attribute_mismatch(tag, key, avalue, bvalue): def optional_float(value): try: - return float(value) + return as_float(value) except: return value @@ -173,8 +175,8 @@ def assert_path_equal(tag, key, avalue, bvalue): def assert_points_equal(tag, key, avalue, bvalue): - alist = [float(value) for pair in avalue.split() for value in pair.split(",")] - blist = [float(value) for pair in bvalue.split() for value in pair.split(",")] + alist = [as_float(value) for pair in avalue.split() for value in pair.split(",")] + blist = [as_float(value) for pair in bvalue.split() for value in pair.split(",")] assert_mixed_list_equal(alist, blist, tag, key, avalue, bvalue) @@ -205,7 +207,7 @@ def assert_dom_equal(a, b, exceptions): continue if exception.get("type", None) == "float": - if not numpy.allclose(float(avalue), float(bvalue)): + if not numpy.allclose(as_float(avalue), as_float(bvalue)): attribute_mismatch(a.tag, akey, avalue, bvalue) elif exception.get("type", None) == "path": assert_path_equal(a.tag, akey, avalue, bvalue) diff --git a/features/steps/tick-locator.py b/features/steps/tick-locator.py index ea5af381..76c96bbc 100644 --- a/features/steps/tick-locator.py +++ b/features/steps/tick-locator.py @@ -7,6 +7,7 @@ import numpy import sys import toyplot.locator +from toyplot.require import as_float import testing @@ -223,7 +224,7 @@ def step_impl(context): @given(u'a {count} {units} interval') def step_impl(context, count, units): - context.timestamp_interval = (float(count), units) + context.timestamp_interval = (as_float(count), units) @given(u'an interval of days') def step_impl(context): diff --git a/toyplot/color.py b/toyplot/color.py index 8a9395a3..eace1220 100644 --- a/toyplot/color.py +++ b/toyplot/color.py @@ -14,7 +14,7 @@ import numpy import toyplot.projection - +from toyplot.require import as_float black = "#292724" """Default color used throughout Toyplot figures.""" @@ -3217,30 +3217,30 @@ def css(value): match = css.rgb_percent(value) if match: - r, g, b = [float(group) / 100.0 for group in match.groups()] + r, g, b = [as_float(group) / 100.0 for group in match.groups()] return rgba(r, g, b, 1) match = css.rgba(value) if match: r, g, b, a = [ - int(group) / 255.0 for group in match.groups()[:3]] + [float(match.groups()[3])] + int(group) / 255.0 for group in match.groups()[:3]] + [as_float(match.groups()[3])] return rgba(r, g, b, a) match = css.rgba_percent(value) if match: r, g, b, a = [ - float(group) / 100.0 for group in match.groups()[:3]] + [float(match.groups()[3])] + as_float(group) / 100.0 for group in match.groups()[:3]] + [as_float(match.groups()[3])] return rgba(r, g, b, a) match = css.hsl(value) if match: - h, s, l = [float(group) for group in match.groups()] + h, s, l = [as_float(group) for group in match.groups()] r, g, b = colorsys.hls_to_rgb((h / 360.0) % 1, l / 100.0, s / 100.0) return rgba(r, g, b, 1) match = css.hsla(value) if match: - h, s, l, a = [float(group) for group in match.groups()] + h, s, l, a = [as_float(group) for group in match.groups()] r, g, b = colorsys.hls_to_rgb((h / 360.0) % 1, l / 100.0, s / 100.0) return rgba(r, g, b, a) diff --git a/toyplot/font.py b/toyplot/font.py index 1b6ad5f1..10affccd 100644 --- a/toyplot/font.py +++ b/toyplot/font.py @@ -89,7 +89,7 @@ def __init__(self, family, size): self._descent = toyplot.units.convert(descent, target="px", default="pt") def __repr__(self): # pragma: no cover - return "" % (self._family, self._size, self.ascent, self.descent) + return "" % (self._family, self._size, self.ascent, self.descent) @property def ascent(self): diff --git a/toyplot/html.py b/toyplot/html.py index 7f16807c..da29aabf 100644 --- a/toyplot/html.py +++ b/toyplot/html.py @@ -29,6 +29,7 @@ import toyplot.mark import toyplot.marker import toyplot.text +from toyplot.require import as_float log = logging.getLogger(__name__) @@ -381,14 +382,14 @@ def _color_fixup(styles): if "fill" in styles: color = toyplot.color.css(styles["fill"]) if color is not None: - opacity = float(styles.get("fill-opacity", 1.0)) + opacity = as_float(styles.get("fill-opacity", 1.0)) styles["fill"] = "rgb(%.3g%%,%.3g%%,%.3g%%)" % ( color["r"] * 100, color["g"] * 100, color["b"] * 100) styles["fill-opacity"] = str(color["a"] * opacity) if "stroke" in styles: color = toyplot.color.css(styles["stroke"]) if color is not None: - opacity = float(styles.get("stroke-opacity", 1.0)) + opacity = as_float(styles.get("stroke-opacity", 1.0)) styles["stroke"] = "rgb(%.3g%%,%.3g%%,%.3g%%)" % ( color["r"] * 100, color["g"] * 100, color["b"] * 100) styles["stroke-opacity"] = str(color["a"] * opacity) @@ -456,9 +457,9 @@ def _draw_text( transform = "" if x or y: - transform += "translate(%r,%r)" % (x, y) + transform += "translate(%s,%s)" % (x, y) if angle: - transform += "rotate(%r)" % (-angle) # pylint: disable=invalid-unary-operand-type + transform += "rotate(%s)" % (-angle) # pylint: disable=invalid-unary-operand-type group = xml.SubElement( root, @@ -613,45 +614,45 @@ def _draw_bar(parent_xml, size, angle=0): markup = xml.SubElement( parent_xml, "line", - y1=repr(-size / 2), - y2=repr(size / 2), + y1=str(-size / 2), + y2=str(size / 2), ) if angle: - markup.set("transform", "rotate(%r)" % (-angle,)) + markup.set("transform", "rotate(%s)" % (-angle,)) def _draw_rect(parent_xml, size, width=1, height=1, angle=0): markup = xml.SubElement( parent_xml, "rect", - x=repr(-size / 2 * width), - y=repr(-size / 2 * height), - width=repr(size * width), - height=repr(size * height), + x=str(-size / 2 * width), + y=str(-size / 2 * height), + width=str(size * width), + height=str(size * height), ) if angle: - markup.set("transform", "rotate(%r)" % (-angle,)) + markup.set("transform", "rotate(%s)" % (-angle,)) def _draw_triangle(parent_xml, size, angle=0): markup = xml.SubElement( parent_xml, "polygon", - points=" ".join(["%r,%r" % (xp, yp) for xp, yp in [ + points=" ".join(["%s,%s" % (xp, yp) for xp, yp in [ (-size / 2, size / 2), (0, -size / 2), (size / 2, size / 2), ]]), ) if angle: - markup.set("transform", "rotate(%r)" % (-angle,)) + markup.set("transform", "rotate(%s)" % (-angle,)) def _draw_circle(parent_xml, size): xml.SubElement( parent_xml, "circle", - r=repr(size / 2), + r=str(size / 2), ) def _draw_marker( @@ -672,9 +673,9 @@ def _draw_marker( xml.SubElement(marker_xml, "title").text = str(title) if transform is None: - transform = "translate(%r, %r)" % (cx, cy) + transform = "translate(%s, %s)" % (cx, cy) if marker.angle: - transform += " rotate(%r)" % (-marker.angle,) + transform += " rotate(%s)" % (-marker.angle,) marker_xml.set("transform", transform) if marker.shape == "|": @@ -709,7 +710,7 @@ def _draw_marker( _draw_rect(marker_xml, marker.size, angle=45) elif marker.shape and marker.shape[0] == "r": width, height = marker.shape[1:].split("x") - _draw_rect(marker_xml, marker.size, width=float(width), height=float(height)) + _draw_rect(marker_xml, marker.size, width=as_float(width), height=as_float(height)) elif marker.shape == "o": _draw_circle(marker_xml, marker.size) elif marker.shape == "oo": @@ -749,7 +750,7 @@ def _draw_marker( { "-toyplot-vertical-align": "middle", "fill": toyplot.color.black, - "font-size": "%rpx" % (marker.size * 0.75), + "font-size": "%spx" % (marker.size * 0.75), "stroke": "none", "text-anchor": "middle", }, @@ -793,9 +794,9 @@ def _render(canvas, context): "xmlns:toyplot": "http://www.sandia.gov/toyplot", "xmlns:xlink": "http://www.w3.org/1999/xlink", }, - width="%rpx" % canvas.width, - height="%rpx" % canvas.height, - viewBox="0 0 %r %r" % (canvas.width, canvas.height), + width="%spx" % canvas.width, + height="%spx" % canvas.height, + viewBox="0 0 %s %s" % (canvas.width, canvas.height), preserveAspectRatio="xMidYMid meet", style=_css_style(canvas._style), id=context.get_id(canvas)) @@ -1157,10 +1158,10 @@ def _render(axis, context): xml.SubElement( axis_xml, "line", - x1=repr(x1), - y1=repr(0), - x2=repr(x2), - y2=repr(0), + x1=str(x1), + y1=str(0), + x2=str(x2), + y2=str(0), style=_css_style( axis.spine._style)) @@ -1177,10 +1178,10 @@ def _render(axis, context): xml.SubElement( ticks_group, "line", - x1=repr(x), - y1=repr(y1), - x2=repr(x), - y2=repr(y2), + x1=str(x), + y1=str(y1), + x2=str(x), + y2=str(y2), style=_css_style( axis.ticks._style, tick_style)) @@ -1265,8 +1266,8 @@ def _render(axis, context): coordinates_xml, "line", x1="0", x2="0", - y1=repr(y1), - y2=repr(y2), + y1=str(y1), + y2=str(y2), style=_css_style(axis.interactive.coordinates.tick.style), ) @@ -1276,7 +1277,7 @@ def _render(axis, context): xml.SubElement( coordinates_xml, "text", x="0", - y=repr(y), + y=str(y), style=_css_style(toyplot.style.combine( {"alignment-baseline": alignment_baseline}, axis.interactive.coordinates.label.style, @@ -1441,10 +1442,10 @@ def _render(numberline, context): xml.SubElement( clip_xml, "rect", - x=repr(0), - y=repr(-height), - width=repr(length), - height=repr(height + numberline.axis._offset), + x=str(0), + y=str(-height), + width=str(length), + height=str(height + numberline.axis._offset), ) children_xml = xml.SubElement( @@ -1483,10 +1484,10 @@ def _render(numberline, colormap, context): xml.SubElement( mark_xml, "rect", - x=repr(x1), - y=repr(-width * 0.5), - width=repr(x2 - x1), - height=repr(width), + x=str(x1), + y=str(-width * 0.5), + width=str(x2 - x1), + height=str(width), style=_css_style({"stroke": "none", "fill": toyplot.color.to_css(color)}), ) @@ -1498,10 +1499,10 @@ def _render(numberline, colormap, context): xml.SubElement( mark_xml, "rect", - x=repr(colormap_range_min), - y=repr(-width * 0.5), - width=repr(colormap_range_max - colormap_range_min), - height=repr(width), + x=str(colormap_range_min), + y=str(-width * 0.5), + width=str(colormap_range_max - colormap_range_min), + height=str(width), style=_css_style(style), ) @@ -1530,10 +1531,10 @@ def _render(numberline, colormap, context): defs_xml, "linearGradient", id="t" + uuid.uuid4().hex, - x1=repr(colormap_range_min), - x2=repr(colormap_range_max), - y1=repr(0), - y2=repr(0), + x1=str(colormap_range_min), + x2=str(colormap_range_max), + y1=str(0), + y2=str(0), gradientUnits="userSpaceOnUse", ) @@ -1560,10 +1561,10 @@ def _render(numberline, colormap, context): xml.SubElement( mark_xml, "rect", - x=repr(colormap_range_min), - y=repr(-width * 0.5), - width=repr(colormap_range_max - colormap_range_min), - height=repr(width), + x=str(colormap_range_min), + y=str(-width * 0.5), + width=str(colormap_range_max - colormap_range_min), + height=str(width), style=_css_style(style), ) @@ -1659,10 +1660,10 @@ def _render(numberline, mark, context): series_xml, "rect", attrib={"class": "toyplot-Datum"}, - x=repr(min(dx1, dx2)), - y=repr(-width * 0.5), - width=repr(numpy.abs(dx1 - dx2)), - height=repr(width), + x=str(min(dx1, dx2)), + y=str(-width * 0.5), + width=str(numpy.abs(dx1 - dx2)), + height=str(width), style=_css_style(dstyle), ) if dtitle is not None: @@ -1678,10 +1679,10 @@ def _render(axes, context): xml.SubElement( clip_xml, "rect", - x=repr(axes._xmin_range - axes.padding), - y=repr(axes._ymin_range - axes.padding), - width=repr(axes._xmax_range - axes._xmin_range + axes.padding * 2), - height=repr(axes._ymax_range - axes._ymin_range + axes.padding * 2), + x=str(axes._xmin_range - axes.padding), + y=str(axes._ymin_range - axes.padding), + width=str(axes._xmax_range - axes._xmin_range + axes.padding * 2), + height=str(axes._ymax_range - axes._ymin_range + axes.padding * 2), ) if axes._hyperlink: @@ -1689,10 +1690,10 @@ def _render(axes, context): xml.SubElement( hyperlink_xml, "rect", - x=repr(axes._xmin_range), - y=repr(axes._ymin_range), - width=repr(axes._xmax_range - axes._xmin_range), - height=repr(axes._ymax_range - axes._ymin_range), + x=str(axes._xmin_range), + y=str(axes._ymin_range), + width=str(axes._xmax_range - axes._xmin_range), + height=str(axes._ymax_range - axes._ymin_range), attrib={"fill": "none", "stroke": "none", "pointer-events": "fill"}, ) @@ -1771,10 +1772,10 @@ def _render(axes, context): cell_xml = xml.SubElement( cell_parent_xml, "rect", - x=repr(cell_left), - y=repr(cell_top), - width=repr(cell_right - cell_left), - height=repr(cell_bottom - cell_top), + x=str(cell_left), + y=str(cell_top), + width=str(cell_right - cell_left), + height=str(cell_bottom - cell_top), style=_css_style({"fill":"transparent", "stroke":"none"}, cell_style), ) @@ -1889,36 +1890,36 @@ def contiguous(a): xml.SubElement( axes_xml, "line", - x1=repr(column_boundaries[start]), - y1=repr(y), - x2=repr(column_boundaries[end]), - y2=repr(y), + x1=str(column_boundaries[start]), + y1=str(y), + x2=str(column_boundaries[end]), + y2=str(y), style=_css_style(axes._gstyle), ) elif line_type == "double": xml.SubElement( axes_xml, "line", - x1=repr( + x1=str( column_boundaries[start]), - y1=repr( + y1=str( y - separation), - x2=repr( + x2=str( column_boundaries[end]), - y2=repr( + y2=str( y - separation), style=_css_style( axes._gstyle)) xml.SubElement( axes_xml, "line", - x1=repr( + x1=str( column_boundaries[start]), - y1=repr( + y1=str( y + separation), - x2=repr( + x2=str( column_boundaries[end]), - y2=repr( + y2=str( y + separation), style=_css_style( axes._gstyle)) @@ -1932,29 +1933,29 @@ def contiguous(a): xml.SubElement( axes_xml, "line", - x1=repr(x), - y1=repr(row_boundaries[start]), - x2=repr(x), - y2=repr(row_boundaries[end]), + x1=str(x), + y1=str(row_boundaries[start]), + x2=str(x), + y2=str(row_boundaries[end]), style=_css_style(axes._gstyle), ) elif line_type == "double": xml.SubElement( axes_xml, "line", - x1=repr(x - separation), - y1=repr(row_boundaries[start]), - x2=repr(x - separation), - y2=repr(row_boundaries[end]), + x1=str(x - separation), + y1=str(row_boundaries[start]), + x2=str(x - separation), + y2=str(row_boundaries[end]), style=_css_style(axes._gstyle), ) xml.SubElement( axes_xml, "line", - x1=repr(x + separation), - y1=repr(row_boundaries[start]), - x2=repr(x + separation), - y2=repr(row_boundaries[end]), + x1=str(x + separation), + y1=str(row_boundaries[start]), + x2=str(x + separation), + y2=str(row_boundaries[end]), style=_css_style(axes._gstyle), ) @@ -2039,10 +2040,10 @@ def _render(axes, mark, context): "rect", attrib={ "class": "toyplot-Datum", - axis1: repr(min(dleft, dright)), - axis2: repr(min(dboundary1, dboundary2)), - distance1: repr(numpy.abs(dleft - dright)), - distance2: repr(numpy.abs(dboundary1 - dboundary2)), + axis1: str(min(dleft, dright)), + axis2: str(min(dboundary1, dboundary2)), + distance1: str(numpy.abs(dleft - dright)), + distance2: str(numpy.abs(dboundary1 - dboundary2)), }, style=_css_style(dstyle), ) @@ -2120,10 +2121,10 @@ def _render(axes, mark, context): "rect", attrib={ "class": "toyplot-Datum", - axis1: repr(min(dleft, dright)), - axis2: repr(min(dboundary1, dboundary2)), - distance1: repr(numpy.abs(dleft - dright)), - distance2: repr(numpy.abs(dboundary1 - dboundary2)), + axis1: str(min(dleft, dright)), + axis2: str(min(dboundary1, dboundary2)), + distance1: str(numpy.abs(dleft - dright)), + distance2: str(numpy.abs(dboundary1 - dboundary2)), }, style=_css_style(dstyle), ) @@ -2175,7 +2176,7 @@ def _render(axes, mark, context): numpy.concatenate((boundary1[segment], boundary2[segment][::-1])), numpy.concatenate((position[segment], position[segment][::-1]))) series_xml = xml.SubElement(mark_xml, "polygon", points=" ".join( - ["%r,%r" % (xi, yi) for xi, yi in coordinates]), style=_css_style(series_style)) + ["%s,%s" % (xi, yi) for xi, yi in coordinates]), style=_css_style(series_style)) if title is not None: xml.SubElement(series_xml, "title").text = str(title) @@ -2221,7 +2222,7 @@ def _render(axes, mark, context): numpy.concatenate((boundary1[segment], boundary2[segment][::-1])), numpy.concatenate((position[segment], position[segment][::-1]))) series_xml = xml.SubElement(mark_xml, "polygon", points=" ".join( - ["%r,%r" % (xi, yi) for xi, yi in coordinates]), style=_css_style(series_style)) + ["%s,%s" % (xi, yi) for xi, yi in coordinates]), style=_css_style(series_style)) if title is not None: xml.SubElement(series_xml, "title").text = str(title) @@ -2267,10 +2268,10 @@ def _render(axes, mark, context): "line", attrib={ "class": "toyplot-Datum", - p1: repr(dposition), - p2: repr(dposition), - b1: repr(boundary1), - b2: repr(boundary2), + p1: str(dposition), + p2: str(dposition), + b1: str(boundary1), + b2: str(boundary2), }, style=_css_style(dstyle), ) @@ -2318,7 +2319,7 @@ def _render(axes, mark, context): x = axes.project("x", dx + p[:,0]) y = axes.project("y", dy + p[:,1]) - points = ["%r,%r" % point for point in zip(x, y)] + points = ["%s,%s" % point for point in zip(x, y)] datum_xml = xml.SubElement( series_xml, @@ -2499,16 +2500,16 @@ def _render(axes, mark, context): # pragma: no cover edge_coordinates[estart+1][0] - edge_coordinates[estart][0], )) - transform = "translate(%r, %r)" % (edge_coordinates[estart][0], edge_coordinates[estart][1]) + transform = "translate(%s, %s)" % (edge_coordinates[estart][0], edge_coordinates[estart][1]) if edge_angle: - transform += " rotate(%r)" % (-edge_angle,) - transform += " translate(%r, 0)" % (marker.size / 2,) + transform += " rotate(%s)" % (-edge_angle,) + transform += " translate(%s, 0)" % (marker.size / 2,) if marker.angle is not None: if isinstance(marker.angle, str) and marker.angle[0:1] == "r": - angle = float(marker.angle[1:]) + angle = as_float(marker.angle[1:]) else: - angle = -edge_angle + float(marker.angle) - transform += " rotate(%r)" % (-angle,) + angle = -edge_angle + as_float(marker.angle) + transform += " rotate(%s)" % (-angle,) _draw_marker( @@ -2540,9 +2541,9 @@ def _render(axes, mark, context): # pragma: no cover )) if marker.angle is not None: if isinstance(marker.angle, str) and marker.angle[0:1] == "r": - angle += float(marker.angle[1:]) + angle += as_float(marker.angle[1:]) else: - angle = float(marker.angle) + angle = as_float(marker.angle) marker = marker + toyplot.marker.create(angle=angle) @@ -2571,16 +2572,16 @@ def _render(axes, mark, context): # pragma: no cover edge_coordinates[end-1][0] - edge_coordinates[end-2][0], )) - transform = "translate(%r, %r)" % (edge_coordinates[end-1][0], edge_coordinates[end-1][1]) + transform = "translate(%s, %s)" % (edge_coordinates[end-1][0], edge_coordinates[end-1][1]) if edge_angle: - transform += " rotate(%r)" % (-edge_angle,) - transform += " translate(%r, 0)" % (-marker.size / 2,) + transform += " rotate(%s)" % (-edge_angle,) + transform += " translate(%s, 0)" % (-marker.size / 2,) if marker.angle is not None: if isinstance(marker.angle, str) and marker.angle[0:1] == "r": - angle = float(marker.angle[1:]) + angle = as_float(marker.angle[1:]) else: - angle = -edge_angle + float(marker.angle) - transform += " rotate(%r)" % (-angle,) + angle = -edge_angle + as_float(marker.angle) + transform += " rotate(%s)" % (-angle,) _draw_marker( @@ -2681,9 +2682,9 @@ def _render(axes, mark, context): for segment in segments: start, stop, step = segment.indices(len(not_null)) for i in range(start, start + 1): - d.append("M %r %r" % (x[i], y[i])) + d.append("M %s %s" % (x[i], y[i])) for i in range(start + 1, stop): - d.append("L %r %r" % (x[i], y[i])) + d.append("L %s %s" % (x[i], y[i])) xml.SubElement( series_xml, "path", @@ -2748,10 +2749,10 @@ def _render(axes, mark, context): series_xml, "rect", attrib={"class": "toyplot-Datum"}, - x=repr(min(dx1, dx2)), - y=repr(min(dy1, dy2)), - width=repr(numpy.abs(dx1 - dx2)), - height=repr(numpy.abs(dy1 - dy2)), + x=str(min(dx1, dx2)), + y=str(min(dy1, dy2)), + width=str(numpy.abs(dx1 - dx2)), + height=str(numpy.abs(dy1 - dy2)), style=_css_style(dstyle), ) if dtitle is not None: @@ -2925,9 +2926,9 @@ def _render(mark, context): xml.SubElement( mark_xml, "image", - x=repr(mark._xmin_range), - y=repr(mark._ymin_range), - width=repr(mark._xmax_range - mark._xmin_range), - height=repr(mark._ymax_range - mark._ymin_range), + x=str(mark._xmin_range), + y=str(mark._ymin_range), + width=str(mark._xmax_range - mark._xmin_range), + height=str(mark._ymax_range - mark._ymin_range), attrib={"xlink:href": toyplot.bitmap.to_png_data_uri(mark._data)}, ) diff --git a/toyplot/layout.py b/toyplot/layout.py index db271965..7eaf7f53 100644 --- a/toyplot/layout.py +++ b/toyplot/layout.py @@ -12,6 +12,7 @@ import numpy import toyplot.units +from toyplot.require import as_float def region( xmin, @@ -70,8 +71,8 @@ def convert(vmin, vmax, value): value = toyplot.units.convert( value, "px", default="px", reference=vmax - vmin) if value < 0: - return float(vmax + value) - return float(vmin + value) + return as_float(vmax + value) + return as_float(vmin + value) # Specify explicit bounds for the region if bounds is not None: diff --git a/toyplot/locator.py b/toyplot/locator.py index f42d9610..e8efd227 100644 --- a/toyplot/locator.py +++ b/toyplot/locator.py @@ -347,7 +347,7 @@ def __init__(self, step=1): def ticks(self, domain_min, domain_max): locations = numpy.arange( domain_min, domain_max + 1, self._step, dtype="int64") - labels = [repr(location) for location in locations] + labels = [str(location) for location in locations] titles = numpy.repeat(None, len(locations)) return locations, labels, titles diff --git a/toyplot/marker.py b/toyplot/marker.py index c07b2366..b60476cf 100644 --- a/toyplot/marker.py +++ b/toyplot/marker.py @@ -12,6 +12,7 @@ import numpy import toyplot.style +from toyplot.require import as_float class Marker(object): @@ -118,8 +119,8 @@ def intersect(self, p): return p if self._shape and self._shape[0] == "r": width, height = self._shape[1:].split("x") - width = float(width) - height = float(height) + width = as_float(width) + height = as_float(height) ap = numpy.abs(p) if ap[1]: @@ -154,11 +155,11 @@ def from_html(html): """Convert a parsed xml.etree.ElementTree representation of a marker to a :class:`toyplot.marker.Marker` object.""" size = html.get("size", None) if size is not None: - size = float(size) + size = as_float(size) angle = html.get("angle", None) if angle is not None: - angle = float(angle) + angle = as_float(angle) return Marker( shape=html.get("shape", None), diff --git a/toyplot/reportlab/__init__.py b/toyplot/reportlab/__init__.py index 9005589a..ffab7558 100644 --- a/toyplot/reportlab/__init__.py +++ b/toyplot/reportlab/__init__.py @@ -15,6 +15,7 @@ import toyplot.color import toyplot.units +from toyplot.require import as_float def render(svg, canvas): @@ -47,8 +48,8 @@ def get_fill(root, style): if color is None: return None, None - fill_opacity = float(style.get("fill-opacity", 1.0)) - opacity = float(style.get("opacity", 1.0)) + fill_opacity = as_float(style.get("fill-opacity", 1.0)) + opacity = as_float(style.get("opacity", 1.0)) fill = toyplot.color.rgba( color["r"], color["g"], @@ -65,8 +66,8 @@ def get_stroke(style): if color is None: return None - stroke_opacity = float(style.get("stroke-opacity", 1.0)) - opacity = float(style.get("opacity", 1.0)) + stroke_opacity = as_float(style.get("stroke-opacity", 1.0)) + opacity = as_float(style.get("opacity", 1.0)) return toyplot.color.rgba( color["r"], color["g"], @@ -144,10 +145,10 @@ def render_element(root, element, canvas, styles): styles.append(current_style) if "stroke-width" in current_style: - canvas.setLineWidth(float(current_style["stroke-width"])) + canvas.setLineWidth(as_float(current_style["stroke-width"])) if "stroke-dasharray" in current_style: - canvas.setDash([float(length) for length in current_style["stroke-dasharray"].split(",")]) + canvas.setDash([as_float(length) for length in current_style["stroke-dasharray"].split(",")]) if current_style.get("visibility") != "hidden": @@ -158,14 +159,14 @@ def render_element(root, element, canvas, styles): arguments = arguments.split(",") if transform.strip() == "translate": if len(arguments) == 2: - canvas.translate(float(arguments[0]), float(arguments[1])) + canvas.translate(as_float(arguments[0]), as_float(arguments[1])) elif transform.strip() == "rotate": if len(arguments) == 1: - canvas.rotate(float(arguments[0])) + canvas.rotate(as_float(arguments[0])) if len(arguments) == 3: - canvas.translate(float(arguments[1]), float(arguments[2])) - canvas.rotate(float(arguments[0])) - canvas.translate(-float(arguments[1]), -float(arguments[2])) + canvas.translate(as_float(arguments[1]), as_float(arguments[2])) + canvas.rotate(as_float(arguments[0])) + canvas.translate(-as_float(arguments[1]), -as_float(arguments[2])) if element.tag == "svg": if "background-color" in current_style: @@ -173,20 +174,20 @@ def render_element(root, element, canvas, styles): canvas.rect( 0, 0, - float(element.get("width")[:-2]), - float(element.get("height")[:-2]), + as_float(element.get("width")[:-2]), + as_float(element.get("height")[:-2]), stroke=0, fill=1, ) if current_style["border-style"] != "none": set_stroke_color(canvas, toyplot.color.css(current_style["border-color"])) - canvas.setLineWidth(float(current_style["border-width"])) + canvas.setLineWidth(as_float(current_style["border-width"])) canvas.rect( 0, 0, - float(element.get("width")[:-2]), - float(element.get("height")[:-2]), + as_float(element.get("width")[:-2]), + as_float(element.get("height")[:-2]), stroke=1, fill=0, ) @@ -205,10 +206,10 @@ def render_element(root, element, canvas, styles): clip_path = root.find(".//*[@id='%s']" % clip_id) for child in clip_path: if child.tag == "rect": - x = float(child.get("x")) - y = float(child.get("y")) - width = float(child.get("width")) - height = float(child.get("height")) + x = as_float(child.get("x")) + y = as_float(child.get("y")) + width = as_float(child.get("width")) + height = as_float(child.get("height")) path = canvas.beginPath() path.moveTo(x, y) path.lineTo(x + width, y) @@ -231,10 +232,10 @@ def render_element(root, element, canvas, styles): set_stroke_color(canvas, stroke) canvas.setLineCap(get_line_cap(current_style)) canvas.line( - float(element.get("x1", 0)), - float(element.get("y1", 0)), - float(element.get("x2", 0)), - float(element.get("y2", 0)), + as_float(element.get("x1", 0)), + as_float(element.get("y1", 0)), + as_float(element.get("x2", 0)), + as_float(element.get("y2", 0)), ) elif element.tag == "path": stroke = get_stroke(current_style) @@ -247,10 +248,10 @@ def render_element(root, element, canvas, styles): command = commands.pop(0) if command == "L": path.lineTo( - float(commands.pop(0)), float(commands.pop(0))) + as_float(commands.pop(0)), as_float(commands.pop(0))) elif command == "M": path.moveTo( - float(commands.pop(0)), float(commands.pop(0))) + as_float(commands.pop(0)), as_float(commands.pop(0))) canvas.drawPath(path) elif element.tag == "polygon": fill, fill_gradient = get_fill(root, current_style) @@ -265,9 +266,9 @@ def render_element(root, element, canvas, styles): points = [point.split(",") for point in element.get("points").split()] path = canvas.beginPath() for point in points[:1]: - path.moveTo(float(point[0]), float(point[1])) + path.moveTo(as_float(point[0]), as_float(point[1])) for point in points[1:]: - path.lineTo(float(point[0]), float(point[1])) + path.lineTo(as_float(point[0]), as_float(point[1])) path.close() canvas.drawPath(path, stroke=stroke is not None, fill=fill is not None) elif element.tag == "rect": @@ -278,10 +279,10 @@ def render_element(root, element, canvas, styles): if stroke is not None: set_stroke_color(canvas, stroke) - x = float(element.get("x", 0)) - y = float(element.get("y", 0)) - width = float(element.get("width")) - height = float(element.get("height")) + x = as_float(element.get("x", 0)) + y = as_float(element.get("y", 0)) + width = as_float(element.get("width")) + height = as_float(element.get("height")) path = canvas.beginPath() path.moveTo(x, y) @@ -294,19 +295,19 @@ def render_element(root, element, canvas, styles): pdf_colors = [] pdf_offsets = [] for stop in fill_gradient: - offset = float(stop.get("offset")) + offset = as_float(stop.get("offset")) color = toyplot.color.css(stop.get("stop-color")) - opacity = float(stop.get("stop-opacity")) + opacity = as_float(stop.get("stop-opacity")) pdf_colors.append(reportlab.lib.colors.Color(color["r"], color["g"], color["b"], color["a"] * opacity)) pdf_offsets.append(offset) canvas.saveState() canvas.clipPath(path, stroke=0, fill=1) canvas.setFillAlpha(1) canvas.linearGradient( - float(fill_gradient.get("x1")), - float(fill_gradient.get("y1")), - float(fill_gradient.get("x2")), - float(fill_gradient.get("y2")), + as_float(fill_gradient.get("x1")), + as_float(fill_gradient.get("y1")), + as_float(fill_gradient.get("x2")), + as_float(fill_gradient.get("y2")), pdf_colors, pdf_offsets, ) @@ -323,13 +324,13 @@ def render_element(root, element, canvas, styles): if stroke is not None: set_stroke_color(canvas, stroke) - cx = float(element.get("cx", 0)) - cy = float(element.get("cy", 0)) - r = float(element.get("r")) + cx = as_float(element.get("cx", 0)) + cy = as_float(element.get("cy", 0)) + r = as_float(element.get("r")) canvas.circle(cx, cy, r, stroke=stroke is not None, fill=fill is not None) elif element.tag == "text": - x = float(element.get("x", 0)) - y = float(element.get("y", 0)) + x = as_float(element.get("x", 0)) + y = as_float(element.get("y", 0)) fill, fill_gradient = get_fill(element, current_style) stroke = get_stroke(current_style) font_family = get_font_family(current_style) @@ -357,10 +358,10 @@ def render_element(root, element, canvas, styles): image = PIL.Image.open(image) image = reportlab.lib.utils.ImageReader(image) - x = float(element.get("x", 0)) - y = float(element.get("y", 0)) - width = float(element.get("width")) - height = float(element.get("height")) + x = as_float(element.get("x", 0)) + y = as_float(element.get("y", 0)) + width = as_float(element.get("width")) + height = as_float(element.get("height")) canvas.saveState() path = canvas.beginPath() diff --git a/toyplot/require.py b/toyplot/require.py index 2d632014..49cf4858 100644 --- a/toyplot/require.py +++ b/toyplot/require.py @@ -110,3 +110,41 @@ def filename(value): def hyperlink(value): """Raise an exception if a value isn't a valid string hyperlink, or None.""" return optional_string(value) + +def as_int(value,precision=None): + """Raise an exception if a value cannot be converted to an int, or value + coerced to a python int. precision is optional but can be 8, 16, etc.""" + + # Try simple conversion; if this fails, move on + try: + return int(value,precision) + except ValueError: + pass + + # If simple conversion failed, try to parse as a np.int64(value) string + try: + return int(value.split("(")[-1].split(")")[0],precision) + except Exception as e: + err = f"'{value}' could not be converted to a float." + raise ValueError(err) from e + + +def as_float(value): + """Raise an exception if a value cannot be converted to a float, or value + coerced to a python float.""" + + # Try simple conversion; if this fails, move on + try: + return float(value) + except ValueError: + pass + + # If simple conversion failed, try to parse as a np.float64(value) string + try: + return float(value.split("(")[-1].split(")")[0]) + except Exception as e: + err = f"'{value}' could not be converted to a float." + raise ValueError(err) from e + + + \ No newline at end of file diff --git a/toyplot/style.py b/toyplot/style.py index 9a2e5e13..1a9427ee 100644 --- a/toyplot/style.py +++ b/toyplot/style.py @@ -10,6 +10,7 @@ import numpy import toyplot.color +from toyplot.require import as_float def require(css, allowed): """Validate that an object is usable as CSS style information. @@ -136,14 +137,14 @@ def _color_fixup(styles): if "fill" in styles: color = toyplot.color.css(styles["fill"]) if color is not None: - opacity = float(styles.get("fill-opacity", 1.0)) + opacity = as_float(styles.get("fill-opacity", 1.0)) styles["fill"] = "rgb(%.3g%%,%.3g%%,%.3g%%)" % ( color["r"] * 100, color["g"] * 100, color["b"] * 100) styles["fill-opacity"] = str(color["a"] * opacity) if "stroke" in styles: color = toyplot.color.css(styles["stroke"]) if color is not None: - opacity = float(styles.get("stroke-opacity", 1.0)) + opacity = as_float(styles.get("stroke-opacity", 1.0)) styles["stroke"] = "rgb(%.3g%%,%.3g%%,%.3g%%)" % ( color["r"] * 100, color["g"] * 100, color["b"] * 100) styles["stroke-opacity"] = str(color["a"] * opacity) diff --git a/toyplot/units.py b/toyplot/units.py index 682141d1..a93f5496 100644 --- a/toyplot/units.py +++ b/toyplot/units.py @@ -8,6 +8,7 @@ import numbers import re +from toyplot.require import as_float def convert(value, target, default=None, reference=None): """Convert quantities using real-world units. @@ -49,7 +50,7 @@ def convert(value, target, default=None, reference=None): if value == "0": return 0 value, units = re.match(r"([^a-zA-Z%]+)([a-zA-Z%]+)\Z", value).groups() - value = (float(value), units) + value = (as_float(value), units) if not isinstance(value, tuple): raise ValueError("Value must be a number, string or (number, string) tuple.")