Python 3 Updates

Ran python-modernizer, this fixed most of the Python 3
incompatibilities.

Had to manually rework the iteration in svg_regex and svg_transform;
.next doesn't exist in Python 3. Wrapped the builtin next function in a
partial for equivalent functionality.
This commit is contained in:
Andy Levisay 2015-04-28 15:26:55 -05:00
parent 85dff51cc9
commit d1c66cc75b
5 changed files with 158 additions and 150 deletions

View file

@ -15,8 +15,3 @@
## limitations under the License. ## limitations under the License.
## ##
############################################################################### ###############################################################################
import scour
import svg_regex
import svg_transform
import yocto_css

View file

@ -47,21 +47,27 @@
# necessary to get true division # necessary to get true division
from __future__ import division from __future__ import division
# Needed for Python 2/3 compatible print function.
from __future__ import print_function
from __future__ import absolute_import
import os import os
import sys import sys
import xml.dom.minidom import xml.dom.minidom
import re import re
import math import math
from svg_regex import svg_parser from .svg_regex import svg_parser
from svg_transform import svg_transform_parser from .svg_transform import svg_transform_parser
import optparse import optparse
from yocto_css import parseCssString from .yocto_css import parseCssString
import six
from six.moves import range
# Python 2.3- did not have Decimal # Python 2.3- did not have Decimal
try: try:
from decimal import * from decimal import Decimal, InvalidOperation, getcontext
except ImportError: except ImportError:
print >>sys.stderr, "Scour requires Python 2.4." print("Scour requires Python 2.4.", file=sys.stderr)
# Import Psyco if available # Import Psyco if available
try: try:
@ -531,7 +537,7 @@ def findReferencingProperty(node, prop, val, ids):
if prop in referencingProps and val != '' : if prop in referencingProps and val != '' :
if len(val) >= 7 and val[0:5] == 'url(#' : if len(val) >= 7 and val[0:5] == 'url(#' :
id = val[5:val.find(')')] id = val[5:val.find(')')]
if ids.has_key(id) : if id in ids :
ids[id][0] += 1 ids[id][0] += 1
ids[id][1].append(node) ids[id][1].append(node)
else: else:
@ -546,7 +552,7 @@ def findReferencingProperty(node, prop, val, ids):
elif val[0:6] == "url('#" : elif val[0:6] == "url('#" :
id = val[6:val.find("')")] id = val[6:val.find("')")]
if id != None: if id != None:
if ids.has_key(id) : if id in ids :
ids[id][0] += 1 ids[id][0] += 1
ids[id][1].append(node) ids[id][1].append(node)
else: else:
@ -762,7 +768,7 @@ def unprotected_ids(doc, options):
protect_ids_list = options.protect_ids_list.split(",") protect_ids_list = options.protect_ids_list.split(",")
if options.protect_ids_prefix: if options.protect_ids_prefix:
protect_ids_prefixes = options.protect_ids_prefix.split(",") protect_ids_prefixes = options.protect_ids_prefix.split(",")
for id in identifiedElements.keys(): for id in list(identifiedElements.keys()):
protected = False protected = False
if options.protect_ids_noninkscape and not id[-1].isdigit(): if options.protect_ids_noninkscape and not id[-1].isdigit():
protected = True protected = True
@ -785,9 +791,9 @@ def removeUnreferencedIDs(referencedIDs, identifiedElements):
global numIDsRemoved global numIDsRemoved
keepTags = ['font'] keepTags = ['font']
num = 0; num = 0;
for id in identifiedElements.keys(): for id in list(identifiedElements.keys()):
node = identifiedElements[id] node = identifiedElements[id]
if referencedIDs.has_key(id) == False and not node.nodeName in keepTags: if (id in referencedIDs) == False and not node.nodeName in keepTags:
node.removeAttribute('id') node.removeAttribute('id')
numIDsRemoved += 1 numIDsRemoved += 1
num += 1 num += 1
@ -800,7 +806,7 @@ def removeNamespacedAttributes(node, namespaces):
# remove all namespace'd attributes from this element # remove all namespace'd attributes from this element
attrList = node.attributes attrList = node.attributes
attrsToRemove = [] attrsToRemove = []
for attrNum in xrange(attrList.length): for attrNum in range(attrList.length):
attr = attrList.item(attrNum) attr = attrList.item(attrNum)
if attr != None and attr.namespaceURI in namespaces: if attr != None and attr.namespaceURI in namespaces:
attrsToRemove.append(attr.nodeName) attrsToRemove.append(attr.nodeName)
@ -915,7 +921,7 @@ def moveCommonAttributesToParentGroup(elem, referencedElements):
# its fill attribute is not what we want to look at, we should look for the first # its fill attribute is not what we want to look at, we should look for the first
# non-animate/set element # non-animate/set element
attrList = childElements[0].attributes attrList = childElements[0].attributes
for num in xrange(attrList.length): for num in range(attrList.length):
attr = attrList.item(num) attr = attrList.item(num)
# this is most of the inheritable properties from http://www.w3.org/TR/SVG11/propidx.html # this is most of the inheritable properties from http://www.w3.org/TR/SVG11/propidx.html
# and http://www.w3.org/TR/SVGTiny12/attributeTable.html # and http://www.w3.org/TR/SVGTiny12/attributeTable.html
@ -934,7 +940,7 @@ def moveCommonAttributesToParentGroup(elem, referencedElements):
commonAttrs[attr.nodeName] = attr.nodeValue commonAttrs[attr.nodeName] = attr.nodeValue
# for each subsequent child element # for each subsequent child element
for childNum in xrange(len(childElements)): for childNum in range(len(childElements)):
# skip first child # skip first child
if childNum == 0: if childNum == 0:
continue continue
@ -946,7 +952,7 @@ def moveCommonAttributesToParentGroup(elem, referencedElements):
distinctAttrs = [] distinctAttrs = []
# loop through all current 'common' attributes # loop through all current 'common' attributes
for name in commonAttrs.keys(): for name in list(commonAttrs.keys()):
# if this child doesn't match that attribute, schedule it for removal # if this child doesn't match that attribute, schedule it for removal
if child.getAttribute(name) != commonAttrs[name]: if child.getAttribute(name) != commonAttrs[name]:
distinctAttrs.append(name) distinctAttrs.append(name)
@ -955,7 +961,7 @@ def moveCommonAttributesToParentGroup(elem, referencedElements):
del commonAttrs[name] del commonAttrs[name]
# commonAttrs now has all the inheritable attributes which are common among all child elements # commonAttrs now has all the inheritable attributes which are common among all child elements
for name in commonAttrs.keys(): for name in list(commonAttrs.keys()):
for child in childElements: for child in childElements:
child.removeAttribute(name) child.removeAttribute(name)
elem.setAttribute(name, commonAttrs[name]) elem.setAttribute(name, commonAttrs[name])
@ -1088,7 +1094,7 @@ def removeUnusedAttributesOnParent(elem):
# get all attribute values on this parent # get all attribute values on this parent
attrList = elem.attributes attrList = elem.attributes
unusedAttrs = {} unusedAttrs = {}
for num in xrange(attrList.length): for num in range(attrList.length):
attr = attrList.item(num) attr = attrList.item(num)
if attr.nodeName in ['clip-rule', if attr.nodeName in ['clip-rule',
'display-align', 'display-align',
@ -1104,10 +1110,10 @@ def removeUnusedAttributesOnParent(elem):
unusedAttrs[attr.nodeName] = attr.nodeValue unusedAttrs[attr.nodeName] = attr.nodeValue
# for each child, if at least one child inherits the parent's attribute, then remove # for each child, if at least one child inherits the parent's attribute, then remove
for childNum in xrange(len(childElements)): for childNum in range(len(childElements)):
child = childElements[childNum] child = childElements[childNum]
inheritedAttrs = [] inheritedAttrs = []
for name in unusedAttrs.keys(): for name in list(unusedAttrs.keys()):
val = child.getAttribute(name) val = child.getAttribute(name)
if val == '' or val == None or val == 'inherit': if val == '' or val == None or val == 'inherit':
inheritedAttrs.append(name) inheritedAttrs.append(name)
@ -1115,7 +1121,7 @@ def removeUnusedAttributesOnParent(elem):
del unusedAttrs[a] del unusedAttrs[a]
# unusedAttrs now has all the parent attributes that are unused # unusedAttrs now has all the parent attributes that are unused
for name in unusedAttrs.keys(): for name in list(unusedAttrs.keys()):
elem.removeAttribute(name) elem.removeAttribute(name)
num += 1 num += 1
@ -1145,7 +1151,7 @@ def removeDuplicateGradientStops(doc):
color = stop.getAttribute('stop-color') color = stop.getAttribute('stop-color')
opacity = stop.getAttribute('stop-opacity') opacity = stop.getAttribute('stop-opacity')
style = stop.getAttribute('style') style = stop.getAttribute('style')
if stops.has_key(offset) : if offset in stops :
oldStop = stops[offset] oldStop = stops[offset]
if oldStop[0] == color and oldStop[1] == opacity and oldStop[2] == style: if oldStop[0] == color and oldStop[1] == opacity and oldStop[2] == style:
stopsToRemove.append(stop) stopsToRemove.append(stop)
@ -1166,7 +1172,7 @@ def collapseSinglyReferencedGradients(doc):
identifiedElements = findElementsWithId(doc.documentElement) identifiedElements = findElementsWithId(doc.documentElement)
# make sure to reset the ref'ed ids for when we are running this in testscour # make sure to reset the ref'ed ids for when we are running this in testscour
for rid,nodeCount in findReferencedElements(doc.documentElement).iteritems(): for rid,nodeCount in six.iteritems(findReferencedElements(doc.documentElement)):
count = nodeCount[0] count = nodeCount[0]
nodes = nodeCount[1] nodes = nodeCount[1]
# Make sure that there's actually a defining element for the current ID name. # Make sure that there's actually a defining element for the current ID name.
@ -1253,7 +1259,7 @@ def removeDuplicateGradients(doc):
# now compare stops # now compare stops
stopsNotEqual = False stopsNotEqual = False
for i in xrange(stops.length): for i in range(stops.length):
if stopsNotEqual: break if stopsNotEqual: break
stop = stops.item(i) stop = stops.item(i)
ostop = ostops.item(i) ostop = ostops.item(i)
@ -1265,16 +1271,16 @@ def removeDuplicateGradients(doc):
# ograd is a duplicate of grad, we schedule it to be removed UNLESS # ograd is a duplicate of grad, we schedule it to be removed UNLESS
# ograd is ALREADY considered a 'master' element # ograd is ALREADY considered a 'master' element
if not gradientsToRemove.has_key(ograd): if ograd not in gradientsToRemove:
if not duplicateToMaster.has_key(ograd): if ograd not in duplicateToMaster:
if not gradientsToRemove.has_key(grad): if grad not in gradientsToRemove:
gradientsToRemove[grad] = [] gradientsToRemove[grad] = []
gradientsToRemove[grad].append( ograd ) gradientsToRemove[grad].append( ograd )
duplicateToMaster[ograd] = grad duplicateToMaster[ograd] = grad
# get a collection of all elements that are referenced and their referencing elements # get a collection of all elements that are referenced and their referencing elements
referencedIDs = findReferencedElements(doc.documentElement) referencedIDs = findReferencedElements(doc.documentElement)
for masterGrad in gradientsToRemove.keys(): for masterGrad in list(gradientsToRemove.keys()):
master_id = masterGrad.getAttribute('id') master_id = masterGrad.getAttribute('id')
# print 'master='+master_id # print 'master='+master_id
for dupGrad in gradientsToRemove[masterGrad]: for dupGrad in gradientsToRemove[masterGrad]:
@ -1322,7 +1328,7 @@ def _getStyle(node):
def _setStyle(node, styleMap): def _setStyle(node, styleMap):
u"""Sets the style attribute of a node to the dictionary ``styleMap``.""" u"""Sets the style attribute of a node to the dictionary ``styleMap``."""
fixedStyle = ';'.join([prop + ':' + styleMap[prop] for prop in styleMap.keys()]) fixedStyle = ';'.join([prop + ':' + styleMap[prop] for prop in list(styleMap.keys())])
if fixedStyle != '' : if fixedStyle != '' :
node.setAttribute('style', fixedStyle) node.setAttribute('style', fixedStyle)
elif node.getAttribute('style'): elif node.getAttribute('style'):
@ -1337,7 +1343,7 @@ def repairStyle(node, options):
# I've seen this enough to know that I need to correct it: # I've seen this enough to know that I need to correct it:
# fill: url(#linearGradient4918) rgb(0, 0, 0); # fill: url(#linearGradient4918) rgb(0, 0, 0);
for prop in ['fill', 'stroke'] : for prop in ['fill', 'stroke'] :
if styleMap.has_key(prop) : if prop in styleMap :
chunk = styleMap[prop].split(') ') chunk = styleMap[prop].split(') ')
if len(chunk) == 2 and (chunk[0][:5] == 'url(#' or chunk[0][:6] == 'url("#' or chunk[0][:6] == "url('#") and chunk[1] == 'rgb(0, 0, 0)' : if len(chunk) == 2 and (chunk[0][:5] == 'url(#' or chunk[0][:6] == 'url("#' or chunk[0][:6] == "url('#") and chunk[1] == 'rgb(0, 0, 0)' :
styleMap[prop] = chunk[0] + ')' styleMap[prop] = chunk[0] + ')'
@ -1345,23 +1351,23 @@ def repairStyle(node, options):
# Here is where we can weed out unnecessary styles like: # Here is where we can weed out unnecessary styles like:
# opacity:1 # opacity:1
if styleMap.has_key('opacity') : if 'opacity' in styleMap :
opacity = float(styleMap['opacity']) opacity = float(styleMap['opacity'])
# if opacity='0' then all fill and stroke properties are useless, remove them # if opacity='0' then all fill and stroke properties are useless, remove them
if opacity == 0.0 : if opacity == 0.0 :
for uselessStyle in ['fill', 'fill-opacity', 'fill-rule', 'stroke', 'stroke-linejoin', for uselessStyle in ['fill', 'fill-opacity', 'fill-rule', 'stroke', 'stroke-linejoin',
'stroke-opacity', 'stroke-miterlimit', 'stroke-linecap', 'stroke-dasharray', 'stroke-opacity', 'stroke-miterlimit', 'stroke-linecap', 'stroke-dasharray',
'stroke-dashoffset', 'stroke-opacity'] : 'stroke-dashoffset', 'stroke-opacity'] :
if styleMap.has_key(uselessStyle): if uselessStyle in styleMap:
del styleMap[uselessStyle] del styleMap[uselessStyle]
num += 1 num += 1
# if stroke:none, then remove all stroke-related properties (stroke-width, etc) # if stroke:none, then remove all stroke-related properties (stroke-width, etc)
# TODO: should also detect if the computed value of this element is stroke="none" # TODO: should also detect if the computed value of this element is stroke="none"
if styleMap.has_key('stroke') and styleMap['stroke'] == 'none' : if 'stroke' in styleMap and styleMap['stroke'] == 'none' :
for strokestyle in [ 'stroke-width', 'stroke-linejoin', 'stroke-miterlimit', for strokestyle in [ 'stroke-width', 'stroke-linejoin', 'stroke-miterlimit',
'stroke-linecap', 'stroke-dasharray', 'stroke-dashoffset', 'stroke-opacity'] : 'stroke-linecap', 'stroke-dasharray', 'stroke-dashoffset', 'stroke-opacity'] :
if styleMap.has_key(strokestyle) : if strokestyle in styleMap :
del styleMap[strokestyle] del styleMap[strokestyle]
num += 1 num += 1
# TODO: This is actually a problem if a parent element has a specified stroke # TODO: This is actually a problem if a parent element has a specified stroke
@ -1369,38 +1375,38 @@ def repairStyle(node, options):
del styleMap['stroke'] del styleMap['stroke']
# if fill:none, then remove all fill-related properties (fill-rule, etc) # if fill:none, then remove all fill-related properties (fill-rule, etc)
if styleMap.has_key('fill') and styleMap['fill'] == 'none' : if 'fill' in styleMap and styleMap['fill'] == 'none' :
for fillstyle in [ 'fill-rule', 'fill-opacity' ] : for fillstyle in [ 'fill-rule', 'fill-opacity' ] :
if styleMap.has_key(fillstyle) : if fillstyle in styleMap :
del styleMap[fillstyle] del styleMap[fillstyle]
num += 1 num += 1
# fill-opacity: 0 # fill-opacity: 0
if styleMap.has_key('fill-opacity') : if 'fill-opacity' in styleMap :
fillOpacity = float(styleMap['fill-opacity']) fillOpacity = float(styleMap['fill-opacity'])
if fillOpacity == 0.0 : if fillOpacity == 0.0 :
for uselessFillStyle in [ 'fill', 'fill-rule' ] : for uselessFillStyle in [ 'fill', 'fill-rule' ] :
if styleMap.has_key(uselessFillStyle): if uselessFillStyle in styleMap:
del styleMap[uselessFillStyle] del styleMap[uselessFillStyle]
num += 1 num += 1
# stroke-opacity: 0 # stroke-opacity: 0
if styleMap.has_key('stroke-opacity') : if 'stroke-opacity' in styleMap :
strokeOpacity = float(styleMap['stroke-opacity']) strokeOpacity = float(styleMap['stroke-opacity'])
if strokeOpacity == 0.0 : if strokeOpacity == 0.0 :
for uselessStrokeStyle in [ 'stroke', 'stroke-width', 'stroke-linejoin', 'stroke-linecap', for uselessStrokeStyle in [ 'stroke', 'stroke-width', 'stroke-linejoin', 'stroke-linecap',
'stroke-dasharray', 'stroke-dashoffset' ] : 'stroke-dasharray', 'stroke-dashoffset' ] :
if styleMap.has_key(uselessStrokeStyle): if uselessStrokeStyle in styleMap:
del styleMap[uselessStrokeStyle] del styleMap[uselessStrokeStyle]
num += 1 num += 1
# stroke-width: 0 # stroke-width: 0
if styleMap.has_key('stroke-width') : if 'stroke-width' in styleMap :
strokeWidth = SVGLength(styleMap['stroke-width']) strokeWidth = SVGLength(styleMap['stroke-width'])
if strokeWidth.value == 0.0 : if strokeWidth.value == 0.0 :
for uselessStrokeStyle in [ 'stroke', 'stroke-linejoin', 'stroke-linecap', for uselessStrokeStyle in [ 'stroke', 'stroke-linejoin', 'stroke-linecap',
'stroke-dasharray', 'stroke-dashoffset', 'stroke-opacity' ] : 'stroke-dasharray', 'stroke-dashoffset', 'stroke-opacity' ] :
if styleMap.has_key(uselessStrokeStyle): if uselessStrokeStyle in styleMap:
del styleMap[uselessStrokeStyle] del styleMap[uselessStrokeStyle]
num += 1 num += 1
@ -1413,18 +1419,18 @@ def repairStyle(node, options):
'text-align', 'text-anchor', 'text-decoration', 'text-align', 'text-anchor', 'text-decoration',
'text-rendering', 'unicode-bidi', 'text-rendering', 'unicode-bidi',
'word-spacing', 'writing-mode'] : 'word-spacing', 'writing-mode'] :
if styleMap.has_key(fontstyle) : if fontstyle in styleMap :
del styleMap[fontstyle] del styleMap[fontstyle]
num += 1 num += 1
# remove inkscape-specific styles # remove inkscape-specific styles
# TODO: need to get a full list of these # TODO: need to get a full list of these
for inkscapeStyle in ['-inkscape-font-specification']: for inkscapeStyle in ['-inkscape-font-specification']:
if styleMap.has_key(inkscapeStyle): if inkscapeStyle in styleMap:
del styleMap[inkscapeStyle] del styleMap[inkscapeStyle]
num += 1 num += 1
if styleMap.has_key('overflow') : if 'overflow' in styleMap :
# overflow specified on element other than svg, marker, pattern # overflow specified on element other than svg, marker, pattern
if not node.nodeName in ['svg','marker','pattern']: if not node.nodeName in ['svg','marker','pattern']:
del styleMap['overflow'] del styleMap['overflow']
@ -1445,7 +1451,7 @@ def repairStyle(node, options):
# now if any of the properties match known SVG attributes we prefer attributes # now if any of the properties match known SVG attributes we prefer attributes
# over style so emit them and remove them from the style map # over style so emit them and remove them from the style map
if options.style_to_xml: if options.style_to_xml:
for propName in styleMap.keys() : for propName in list(styleMap.keys()) :
if propName in svgAttributes : if propName in svgAttributes :
node.setAttribute(propName, styleMap[propName]) node.setAttribute(propName, styleMap[propName])
del styleMap[propName] del styleMap[propName]
@ -1594,7 +1600,7 @@ def removeDefaultAttributeValues(node, options, tainted=set()):
for i in range(node.attributes.length)] for i in range(node.attributes.length)]
for attribute in attributes: for attribute in attributes:
if attribute not in tainted: if attribute not in tainted:
if attribute in default_attributes.keys(): if attribute in list(default_attributes.keys()):
if node.getAttribute(attribute) == default_attributes[attribute]: if node.getAttribute(attribute) == default_attributes[attribute]:
node.removeAttribute(attribute) node.removeAttribute(attribute)
num += 1 num += 1
@ -1602,9 +1608,9 @@ def removeDefaultAttributeValues(node, options, tainted=set()):
tainted = taint(tainted, attribute) tainted = taint(tainted, attribute)
# These attributes might also occur as styles # These attributes might also occur as styles
styles = _getStyle(node) styles = _getStyle(node)
for attribute in styles.keys(): for attribute in list(styles.keys()):
if attribute not in tainted: if attribute not in tainted:
if attribute in default_attributes.keys(): if attribute in list(default_attributes.keys()):
if styles[attribute] == default_attributes[attribute]: if styles[attribute] == default_attributes[attribute]:
del styles[attribute] del styles[attribute]
num += 1 num += 1
@ -1626,7 +1632,7 @@ def convertColor(value):
""" """
s = value s = value
if s in colors.keys(): if s in list(colors.keys()):
s = colors[s] s = colors[s]
rgbpMatch = rgbp.match(s) rgbpMatch = rgbp.match(s)
@ -1680,7 +1686,7 @@ def convertColors(element) :
element.setAttribute(attr, newColorValue) element.setAttribute(attr, newColorValue)
numBytes += (oldBytes - len(element.getAttribute(attr))) numBytes += (oldBytes - len(element.getAttribute(attr)))
# colors might also hide in styles # colors might also hide in styles
if attr in styles.keys(): if attr in list(styles.keys()):
oldColorValue = styles[attr] oldColorValue = styles[attr]
newColorValue = convertColor(oldColorValue) newColorValue = convertColor(oldColorValue)
oldBytes = len(oldColorValue) oldBytes = len(oldColorValue)
@ -1722,13 +1728,13 @@ def cleanPath(element, options) :
# convert absolute coordinates into relative ones. # convert absolute coordinates into relative ones.
# Reuse the data structure 'path', since we're not adding or removing subcommands. # Reuse the data structure 'path', since we're not adding or removing subcommands.
# Also reuse the coordinate lists since we're not adding or removing any. # Also reuse the coordinate lists since we're not adding or removing any.
for pathIndex in xrange(0, len(path)): for pathIndex in range(0, len(path)):
cmd, data = path[pathIndex] # Changes to cmd don't get through to the data structure cmd, data = path[pathIndex] # Changes to cmd don't get through to the data structure
i = 0 i = 0
# adjust abs to rel # adjust abs to rel
# only the A command has some values that we don't want to adjust (radii, rotation, flags) # only the A command has some values that we don't want to adjust (radii, rotation, flags)
if cmd == 'A': if cmd == 'A':
for i in xrange(i, len(data), 7): for i in range(i, len(data), 7):
data[i+5] -= x data[i+5] -= x
data[i+6] -= y data[i+6] -= y
x += data[i+5] x += data[i+5]
@ -1738,14 +1744,14 @@ def cleanPath(element, options) :
x += sum(data[5::7]) x += sum(data[5::7])
y += sum(data[6::7]) y += sum(data[6::7])
elif cmd == 'H': elif cmd == 'H':
for i in xrange(i, len(data)): for i in range(i, len(data)):
data[i] -= x data[i] -= x
x += data[i] x += data[i]
path[pathIndex] = ('h', data) path[pathIndex] = ('h', data)
elif cmd == 'h': elif cmd == 'h':
x += sum(data) x += sum(data)
elif cmd == 'V': elif cmd == 'V':
for i in xrange(i, len(data)): for i in range(i, len(data)):
data[i] -= y data[i] -= y
y += data[i] y += data[i]
path[pathIndex] = ('v', data) path[pathIndex] = ('v', data)
@ -1761,14 +1767,14 @@ def cleanPath(element, options) :
x, y = startx, starty x, y = startx, starty
i = 2 i = 2
for i in xrange(i, len(data), 2): for i in range(i, len(data), 2):
data[i] -= x data[i] -= x
data[i+1] -= y data[i+1] -= y
x += data[i] x += data[i]
y += data[i+1] y += data[i+1]
path[pathIndex] = ('m', data) path[pathIndex] = ('m', data)
elif cmd in ['L','T']: elif cmd in ['L','T']:
for i in xrange(i, len(data), 2): for i in range(i, len(data), 2):
data[i] -= x data[i] -= x
data[i+1] -= y data[i+1] -= y
x += data[i] x += data[i]
@ -1784,14 +1790,14 @@ def cleanPath(element, options) :
else: else:
startx = x + data[0] startx = x + data[0]
starty = y + data[1] starty = y + data[1]
for i in xrange(i, len(data), 2): for i in range(i, len(data), 2):
x += data[i] x += data[i]
y += data[i+1] y += data[i+1]
elif cmd in ['l','t']: elif cmd in ['l','t']:
x += sum(data[0::2]) x += sum(data[0::2])
y += sum(data[1::2]) y += sum(data[1::2])
elif cmd in ['S','Q']: elif cmd in ['S','Q']:
for i in xrange(i, len(data), 4): for i in range(i, len(data), 4):
data[i] -= x data[i] -= x
data[i+1] -= y data[i+1] -= y
data[i+2] -= x data[i+2] -= x
@ -1803,7 +1809,7 @@ def cleanPath(element, options) :
x += sum(data[2::4]) x += sum(data[2::4])
y += sum(data[3::4]) y += sum(data[3::4])
elif cmd == 'C': elif cmd == 'C':
for i in xrange(i, len(data), 6): for i in range(i, len(data), 6):
data[i] -= x data[i] -= x
data[i+1] -= y data[i+1] -= y
data[i+2] -= x data[i+2] -= x
@ -1824,7 +1830,7 @@ def cleanPath(element, options) :
# Reuse the data structure 'path' and the coordinate lists, even if we're # Reuse the data structure 'path' and the coordinate lists, even if we're
# deleting items, because these deletions are relatively cheap. # deleting items, because these deletions are relatively cheap.
if not withRoundLineCaps: if not withRoundLineCaps:
for pathIndex in xrange(0, len(path)): for pathIndex in range(0, len(path)):
cmd, data = path[pathIndex] cmd, data = path[pathIndex]
i = 0 i = 0
if cmd in ['m','l','t']: if cmd in ['m','l','t']:
@ -2059,7 +2065,7 @@ def cleanPath(element, options) :
# Reuse the data structure 'path', since we're not adding or removing subcommands. # Reuse the data structure 'path', since we're not adding or removing subcommands.
# Also reuse the coordinate lists, even if we're deleting items, because these # Also reuse the coordinate lists, even if we're deleting items, because these
# deletions are relatively cheap. # deletions are relatively cheap.
for pathIndex in xrange(1, len(path)): for pathIndex in range(1, len(path)):
cmd, data = path[pathIndex] cmd, data = path[pathIndex]
if cmd in ['h','v'] and len(data) > 1: if cmd in ['h','v'] and len(data) > 1:
coordIndex = 1 coordIndex = 1
@ -2120,7 +2126,7 @@ def parseListOfPoints(s):
# also, if 100-100 is found, split it into two also # also, if 100-100 is found, split it into two also
# <polygon points="100,-100,100-100,100-100-100,-100-100" /> # <polygon points="100,-100,100-100,100-100-100,-100-100" />
for i in xrange(len(ws_nums)): for i in range(len(ws_nums)):
negcoords = ws_nums[i].split("-") negcoords = ws_nums[i].split("-")
# this string didn't have any negative coordinates # this string didn't have any negative coordinates
@ -2128,7 +2134,7 @@ def parseListOfPoints(s):
nums.append(negcoords[0]) nums.append(negcoords[0])
# we got negative coords # we got negative coords
else: else:
for j in xrange(len(negcoords)): for j in range(len(negcoords)):
# first number could be positive # first number could be positive
if j == 0: if j == 0:
if negcoords[0] != '': if negcoords[0] != '':
@ -2152,7 +2158,7 @@ def parseListOfPoints(s):
try: try:
nums[i] = getcontext().create_decimal(nums[i]) nums[i] = getcontext().create_decimal(nums[i])
nums[i + 1] = getcontext().create_decimal(nums[i + 1]) nums[i + 1] = getcontext().create_decimal(nums[i + 1])
except decimal.InvalidOperation: # one of the lengths had a unit or is an invalid number except InvalidOperation: # one of the lengths had a unit or is an invalid number
return [] return []
i += 2 i += 2
@ -2251,7 +2257,7 @@ def scourCoordinates(data, options, forceCommaWsp = False):
# separate from the next number. # separate from the next number.
if options.renderer_workaround: if options.renderer_workaround:
if len(newData) > 0: if len(newData) > 0:
for i in xrange(1, len(newData)): for i in range(1, len(newData)):
if newData[i][0] == '-' and 'e' in newData[i - 1]: if newData[i][0] == '-' and 'e' in newData[i - 1]:
newData[i - 1] += ' ' newData[i - 1] += ' '
return ''.join(newData) return ''.join(newData)
@ -2290,7 +2296,7 @@ def scourUnitlessLength(length, needsRendererWorkaround=False): # length is of a
# gather the non-scientific notation version of the coordinate. # gather the non-scientific notation version of the coordinate.
# this may actually be in scientific notation if the value is # this may actually be in scientific notation if the value is
# sufficiently large or small, so this is a misnomer. # sufficiently large or small, so this is a misnomer.
nonsci = unicode(length).lower().replace("e+", "e") nonsci = six.text_type(length).lower().replace("e+", "e")
if not needsRendererWorkaround: if not needsRendererWorkaround:
if len(nonsci) > 2 and nonsci[:2] == '0.': if len(nonsci) > 2 and nonsci[:2] == '0.':
nonsci = nonsci[1:] # remove the 0, leave the dot nonsci = nonsci[1:] # remove the 0, leave the dot
@ -2300,7 +2306,7 @@ def scourUnitlessLength(length, needsRendererWorkaround=False): # length is of a
if len(nonsci) > 3: # avoid calling normalize unless strictly necessary if len(nonsci) > 3: # avoid calling normalize unless strictly necessary
# and then the scientific notation version, with E+NUMBER replaced with # and then the scientific notation version, with E+NUMBER replaced with
# just eNUMBER, since SVG accepts this. # just eNUMBER, since SVG accepts this.
sci = unicode(length.normalize()).lower().replace("e+", "e") sci = six.text_type(length.normalize()).lower().replace("e+", "e")
if len(sci) < len(nonsci): return sci if len(sci) < len(nonsci): return sci
else: return nonsci else: return nonsci
@ -2337,7 +2343,7 @@ def reducePrecision(element) :
num += len(val) - len(newVal) num += len(val) - len(newVal)
element.setAttribute(lengthAttr, newVal) element.setAttribute(lengthAttr, newVal)
# repeat for attributes hidden in styles # repeat for attributes hidden in styles
if lengthAttr in styles.keys(): if lengthAttr in list(styles.keys()):
val = styles[lengthAttr] val = styles[lengthAttr]
valLen = SVGLength(val) valLen = SVGLength(val)
if valLen.units != Unit.INVALID: if valLen.units != Unit.INVALID:
@ -2714,7 +2720,7 @@ def remapNamespacePrefix(node, oldprefix, newprefix):
# add all the attributes # add all the attributes
attrList = node.attributes attrList = node.attributes
for i in xrange(attrList.length): for i in range(attrList.length):
attr = attrList.item(i) attr = attrList.item(i)
newNode.setAttributeNS( attr.namespaceURI, attr.localName, attr.nodeValue) newNode.setAttributeNS( attr.namespaceURI, attr.localName, attr.nodeValue)
@ -2778,7 +2784,7 @@ def serializeXML(element, options, ind = 0, preserveWhitespace = False):
# now serialize the other attributes # now serialize the other attributes
attrList = element.attributes attrList = element.attributes
for num in xrange(attrList.length) : for num in range(attrList.length) :
attr = attrList.item(num) attr = attrList.item(num)
if attr.nodeName == 'id' or attr.nodeName == 'xml:id': continue if attr.nodeName == 'id' or attr.nodeName == 'xml:id': continue
# if the attribute value contains a double-quote, use single-quotes # if the attribute value contains a double-quote, use single-quotes
@ -2877,7 +2883,7 @@ def scourString(in_string, options=None):
# remove the xmlns: declarations now # remove the xmlns: declarations now
xmlnsDeclsToRemove = [] xmlnsDeclsToRemove = []
attrList = doc.documentElement.attributes attrList = doc.documentElement.attributes
for num in xrange(attrList.length) : for num in range(attrList.length) :
if attrList.item(num).nodeValue in unwanted_ns : if attrList.item(num).nodeValue in unwanted_ns :
xmlnsDeclsToRemove.append(attrList.item(num).nodeName) xmlnsDeclsToRemove.append(attrList.item(num).nodeName)
@ -2895,7 +2901,7 @@ def scourString(in_string, options=None):
attrList = doc.documentElement.attributes attrList = doc.documentElement.attributes
xmlnsDeclsToRemove = [] xmlnsDeclsToRemove = []
redundantPrefixes = [] redundantPrefixes = []
for i in xrange(attrList.length): for i in range(attrList.length):
attr = attrList.item(i) attr = attrList.item(i)
name = attr.nodeName name = attr.nodeName
val = attr.nodeValue val = attr.nodeValue
@ -3174,7 +3180,7 @@ def maybe_gziped_file(filename, mode="r"):
if os.path.splitext(filename)[1].lower() in (".svgz", ".gz"): if os.path.splitext(filename)[1].lower() in (".svgz", ".gz"):
import gzip import gzip
return gzip.GzipFile(filename, mode) return gzip.GzipFile(filename, mode)
return file(filename, mode) return open(filename, mode)
@ -3245,7 +3251,7 @@ def start(options, input, output):
start = get_tick() start = get_tick()
if not options.quiet: if not options.quiet:
print >>sys.stderr, "%s %s\n%s" % (APP, VER, COPYRIGHT) print("%s %s\n%s" % (APP, VER, COPYRIGHT), file=sys.stderr)
# do the work # do the work
in_string = input.read() in_string = input.read()
@ -3260,15 +3266,15 @@ def start(options, input, output):
# GZ: not using globals would be good too # GZ: not using globals would be good too
if not options.quiet: if not options.quiet:
print >>sys.stderr, ' File:', input.name, \ print(' File:', input.name, \
os.linesep + ' Time taken:', str(end-start) + 's' + os.linesep, \ os.linesep + ' Time taken:', str(end-start) + 's' + os.linesep, \
getReport() getReport(), file=sys.stderr)
oldsize = len(in_string) oldsize = len(in_string)
newsize = len(out_string) newsize = len(out_string)
sizediff = (newsize / oldsize) * 100 sizediff = (newsize / oldsize) * 100
print >>sys.stderr, ' Original file size:', oldsize, 'bytes;', \ print(' Original file size:', oldsize, 'bytes;', \
'new file size:', newsize, 'bytes (' + str(sizediff)[:5] + '%)' 'new file size:', newsize, 'bytes (' + str(sizediff)[:5] + '%)', file=sys.stderr)

View file

@ -41,10 +41,11 @@ Out[4]: [('M', [(0.60509999999999997, 0.5)])]
In [5]: svg_parser.parse('M 100-200') # Another edge case In [5]: svg_parser.parse('M 100-200') # Another edge case
Out[5]: [('M', [(100.0, -200.0)])] Out[5]: [('M', [(100.0, -200.0)])]
""" """
from __future__ import absolute_import
import re import re
from decimal import * from decimal import *
from functools import partial
# Sentinel. # Sentinel.
class _EOF(object): class _EOF(object):
@ -145,140 +146,141 @@ class SVGPathParser(object):
def parse(self, text): def parse(self, text):
""" Parse a string of SVG <path> data. """ Parse a string of SVG <path> data.
""" """
next = self.lexer.lex(text).next gen = self.lexer.lex(text)
token = next() next_val_fn = partial(next, *(gen,))
return self.rule_svg_path(next, token) token = next_val_fn()
return self.rule_svg_path(next_val_fn, token)
def rule_svg_path(self, next, token): def rule_svg_path(self, next_val_fn, token):
commands = [] commands = []
while token[0] is not EOF: while token[0] is not EOF:
if token[0] != 'command': if token[0] != 'command':
raise SyntaxError("expecting a command; got %r" % (token,)) raise SyntaxError("expecting a command; got %r" % (token,))
rule = self.command_dispatch[token[1]] rule = self.command_dispatch[token[1]]
command_group, token = rule(next, token) command_group, token = rule(next_val_fn, token)
commands.append(command_group) commands.append(command_group)
return commands return commands
def rule_closepath(self, next, token): def rule_closepath(self, next_val_fn, token):
command = token[1] command = token[1]
token = next() token = next_val_fn()
return (command, []), token return (command, []), token
def rule_moveto_or_lineto(self, next, token): def rule_moveto_or_lineto(self, next_val_fn, token):
command = token[1] command = token[1]
token = next() token = next_val_fn()
coordinates = [] coordinates = []
while token[0] in self.number_tokens: while token[0] in self.number_tokens:
pair, token = self.rule_coordinate_pair(next, token) pair, token = self.rule_coordinate_pair(next_val_fn, token)
coordinates.extend(pair) coordinates.extend(pair)
return (command, coordinates), token return (command, coordinates), token
def rule_orthogonal_lineto(self, next, token): def rule_orthogonal_lineto(self, next_val_fn, token):
command = token[1] command = token[1]
token = next() token = next_val_fn()
coordinates = [] coordinates = []
while token[0] in self.number_tokens: while token[0] in self.number_tokens:
coord, token = self.rule_coordinate(next, token) coord, token = self.rule_coordinate(next_val_fn, token)
coordinates.append(coord) coordinates.append(coord)
return (command, coordinates), token return (command, coordinates), token
def rule_curveto3(self, next, token): def rule_curveto3(self, next_val_fn, token):
command = token[1] command = token[1]
token = next() token = next_val_fn()
coordinates = [] coordinates = []
while token[0] in self.number_tokens: while token[0] in self.number_tokens:
pair1, token = self.rule_coordinate_pair(next, token) pair1, token = self.rule_coordinate_pair(next_val_fn, token)
pair2, token = self.rule_coordinate_pair(next, token) pair2, token = self.rule_coordinate_pair(next_val_fn, token)
pair3, token = self.rule_coordinate_pair(next, token) pair3, token = self.rule_coordinate_pair(next_val_fn, token)
coordinates.extend(pair1) coordinates.extend(pair1)
coordinates.extend(pair2) coordinates.extend(pair2)
coordinates.extend(pair3) coordinates.extend(pair3)
return (command, coordinates), token return (command, coordinates), token
def rule_curveto2(self, next, token): def rule_curveto2(self, next_val_fn, token):
command = token[1] command = token[1]
token = next() token = next_val_fn()
coordinates = [] coordinates = []
while token[0] in self.number_tokens: while token[0] in self.number_tokens:
pair1, token = self.rule_coordinate_pair(next, token) pair1, token = self.rule_coordinate_pair(next_val_fn, token)
pair2, token = self.rule_coordinate_pair(next, token) pair2, token = self.rule_coordinate_pair(next_val_fn, token)
coordinates.extend(pair1) coordinates.extend(pair1)
coordinates.extend(pair2) coordinates.extend(pair2)
return (command, coordinates), token return (command, coordinates), token
def rule_curveto1(self, next, token): def rule_curveto1(self, next_val_fn, token):
command = token[1] command = token[1]
token = next() token = next_val_fn()
coordinates = [] coordinates = []
while token[0] in self.number_tokens: while token[0] in self.number_tokens:
pair1, token = self.rule_coordinate_pair(next, token) pair1, token = self.rule_coordinate_pair(next_val_fn, token)
coordinates.extend(pair1) coordinates.extend(pair1)
return (command, coordinates), token return (command, coordinates), token
def rule_elliptical_arc(self, next, token): def rule_elliptical_arc(self, next_val_fn, token):
command = token[1] command = token[1]
token = next() token = next_val_fn()
arguments = [] arguments = []
while token[0] in self.number_tokens: while token[0] in self.number_tokens:
rx = Decimal(token[1]) * 1 rx = Decimal(token[1]) * 1
if rx < Decimal("0.0"): if rx < Decimal("0.0"):
raise SyntaxError("expecting a nonnegative number; got %r" % (token,)) raise SyntaxError("expecting a nonnegative number; got %r" % (token,))
token = next() token = next_val_fn()
if token[0] not in self.number_tokens: if token[0] not in self.number_tokens:
raise SyntaxError("expecting a number; got %r" % (token,)) raise SyntaxError("expecting a number; got %r" % (token,))
ry = Decimal(token[1]) * 1 ry = Decimal(token[1]) * 1
if ry < Decimal("0.0"): if ry < Decimal("0.0"):
raise SyntaxError("expecting a nonnegative number; got %r" % (token,)) raise SyntaxError("expecting a nonnegative number; got %r" % (token,))
token = next() token = next_val_fn()
if token[0] not in self.number_tokens: if token[0] not in self.number_tokens:
raise SyntaxError("expecting a number; got %r" % (token,)) raise SyntaxError("expecting a number; got %r" % (token,))
axis_rotation = Decimal(token[1]) * 1 axis_rotation = Decimal(token[1]) * 1
token = next() token = next_val_fn()
if token[1] not in ('0', '1'): if token[1] not in ('0', '1'):
raise SyntaxError("expecting a boolean flag; got %r" % (token,)) raise SyntaxError("expecting a boolean flag; got %r" % (token,))
large_arc_flag = Decimal(token[1]) * 1 large_arc_flag = Decimal(token[1]) * 1
token = next() token = next_val_fn()
if token[1] not in ('0', '1'): if token[1] not in ('0', '1'):
raise SyntaxError("expecting a boolean flag; got %r" % (token,)) raise SyntaxError("expecting a boolean flag; got %r" % (token,))
sweep_flag = Decimal(token[1]) * 1 sweep_flag = Decimal(token[1]) * 1
token = next() token = next_val_fn()
if token[0] not in self.number_tokens: if token[0] not in self.number_tokens:
raise SyntaxError("expecting a number; got %r" % (token,)) raise SyntaxError("expecting a number; got %r" % (token,))
x = Decimal(token[1]) * 1 x = Decimal(token[1]) * 1
token = next() token = next_val_fn()
if token[0] not in self.number_tokens: if token[0] not in self.number_tokens:
raise SyntaxError("expecting a number; got %r" % (token,)) raise SyntaxError("expecting a number; got %r" % (token,))
y = Decimal(token[1]) * 1 y = Decimal(token[1]) * 1
token = next() token = next_val_fn()
arguments.extend([rx, ry, axis_rotation, large_arc_flag, sweep_flag, x, y]) arguments.extend([rx, ry, axis_rotation, large_arc_flag, sweep_flag, x, y])
return (command, arguments), token return (command, arguments), token
def rule_coordinate(self, next, token): def rule_coordinate(self, next_val_fn, token):
if token[0] not in self.number_tokens: if token[0] not in self.number_tokens:
raise SyntaxError("expecting a number; got %r" % (token,)) raise SyntaxError("expecting a number; got %r" % (token,))
x = getcontext().create_decimal(token[1]) x = getcontext().create_decimal(token[1])
token = next() token = next_val_fn()
return x, token return x, token
def rule_coordinate_pair(self, next, token): def rule_coordinate_pair(self, next_val_fn, token):
# Inline these since this rule is so common. # Inline these since this rule is so common.
if token[0] not in self.number_tokens: if token[0] not in self.number_tokens:
raise SyntaxError("expecting a number; got %r" % (token,)) raise SyntaxError("expecting a number; got %r" % (token,))
x = getcontext().create_decimal(token[1]) x = getcontext().create_decimal(token[1])
token = next() token = next_val_fn()
if token[0] not in self.number_tokens: if token[0] not in self.number_tokens:
raise SyntaxError("expecting a number; got %r" % (token,)) raise SyntaxError("expecting a number; got %r" % (token,))
y = getcontext().create_decimal(token[1]) y = getcontext().create_decimal(token[1])
token = next() token = next_val_fn()
return [x, y], token return [x, y], token

View file

@ -56,9 +56,12 @@ Multiple transformations are supported:
In [12]: svg_transform_parser.parse('translate(30 -30) rotate(36)') In [12]: svg_transform_parser.parse('translate(30 -30) rotate(36)')
Out[12]: [('translate', [30.0, -30.0]), ('rotate', [36.0])] Out[12]: [('translate', [30.0, -30.0]), ('rotate', [36.0])]
""" """
from __future__ import absolute_import
import re import re
from decimal import * from decimal import *
from six.moves import range
from functools import partial
# Sentinel. # Sentinel.
@ -145,88 +148,90 @@ class SVGTransformationParser(object):
def parse(self, text): def parse(self, text):
""" Parse a string of SVG transform="" data. """ Parse a string of SVG transform="" data.
""" """
next = self.lexer.lex(text).next gen = self.lexer.lex(text)
next_val_fn = partial(next, *(gen,))
commands = [] commands = []
token = next() token = next_val_fn()
while token[0] is not EOF: while token[0] is not EOF:
command, token = self.rule_svg_transform(next, token) command, token = self.rule_svg_transform(next_val_fn, token)
commands.append(command) commands.append(command)
return commands return commands
def rule_svg_transform(self, next, token): def rule_svg_transform(self, next_val_fn, token):
if token[0] != 'command': if token[0] != 'command':
raise SyntaxError("expecting a transformation type; got %r" % (token,)) raise SyntaxError("expecting a transformation type; got %r" % (token,))
command = token[1] command = token[1]
rule = self.command_dispatch[command] rule = self.command_dispatch[command]
token = next() token = next_val_fn()
if token[0] != 'coordstart': if token[0] != 'coordstart':
raise SyntaxError("expecting '('; got %r" % (token,)) raise SyntaxError("expecting '('; got %r" % (token,))
numbers, token = rule(next, token) numbers, token = rule(next_val_fn, token)
if token[0] != 'coordend': if token[0] != 'coordend':
raise SyntaxError("expecting ')'; got %r" % (token,)) raise SyntaxError("expecting ')'; got %r" % (token,))
token = next() token = next_val_fn()
return (command, numbers), token return (command, numbers), token
def rule_1or2numbers(self, next, token): def rule_1or2numbers(self, next_val_fn, token):
numbers = [] numbers = []
# 1st number is mandatory # 1st number is mandatory
token = next() token = next_val_fn()
number, token = self.rule_number(next, token) number, token = self.rule_number(next_val_fn, token)
numbers.append(number) numbers.append(number)
# 2nd number is optional # 2nd number is optional
number, token = self.rule_optional_number(next, token) number, token = self.rule_optional_number(next_val_fn, token)
if number is not None: if number is not None:
numbers.append(number) numbers.append(number)
return numbers, token return numbers, token
def rule_1number(self, next, token): def rule_1number(self, next_val_fn, token):
# this number is mandatory # this number is mandatory
token = next() token = next_val_fn()
number, token = self.rule_number(next, token) number, token = self.rule_number(next_val_fn, token)
numbers = [number] numbers = [number]
return numbers, token return numbers, token
def rule_1or3numbers(self, next, token): def rule_1or3numbers(self, next_val_fn, token):
numbers = [] numbers = []
# 1st number is mandatory # 1st number is mandatory
token = next() token = next_val_fn()
number, token = self.rule_number(next, token) number, token = self.rule_number(next_val_fn, token)
numbers.append(number) numbers.append(number)
# 2nd number is optional # 2nd number is optional
number, token = self.rule_optional_number(next, token) number, token = self.rule_optional_number(next_val_fn, token)
if number is not None: if number is not None:
# but, if the 2nd number is provided, the 3rd is mandatory. # but, if the 2nd number is provided, the 3rd is mandatory.
# we can't have just 2. # we can't have just 2.
numbers.append(number) numbers.append(number)
number, token = self.rule_number(next, token) number, token = self.rule_number(next_val_fn, token)
numbers.append(number) numbers.append(number)
return numbers, token return numbers, token
def rule_6numbers(self, next, token): def rule_6numbers(self, next_val_fn, token):
numbers = [] numbers = []
token = next() token = next_val_fn()
# all numbers are mandatory # all numbers are mandatory
for i in xrange(6): for i in range(6):
number, token = self.rule_number(next, token) number, token = self.rule_number(next_val_fn, token)
numbers.append(number) numbers.append(number)
return numbers, token return numbers, token
def rule_number(self, next, token): def rule_number(self, next_val_fn, token):
if token[0] not in self.number_tokens: if token[0] not in self.number_tokens:
raise SyntaxError("expecting a number; got %r" % (token,)) raise SyntaxError("expecting a number; got %r" % (token,))
x = Decimal(token[1]) * 1 x = Decimal(token[1]) * 1
token = next() token = next_val_fn()
return x, token return x, token
def rule_optional_number(self, next, token): def rule_optional_number(self, next_val_fn, token):
if token[0] not in self.number_tokens: if token[0] not in self.number_tokens:
return None, token return None, token
else: else:
x = Decimal(token[1]) * 1 x = Decimal(token[1]) * 1
token = next() token = next_val_fn()
return x, token return x, token

View file

@ -41,7 +41,7 @@ setup (
author_email = 'codedread@gmail.com', author_email = 'codedread@gmail.com',
url = 'https://github.com/oberstet/scour', url = 'https://github.com/oberstet/scour',
platforms = ('Any'), platforms = ('Any'),
install_requires = [], install_requires = ['six>=1.9.0'],
packages = find_packages(), packages = find_packages(),
zip_safe = True, zip_safe = True,
entry_points = { entry_points = {