settingsValidators: fix code style, adjust strings
Some absolute thonkers in here like two-spaces indent code. Yikes my man.
This commit is contained in:
@@ -1,29 +1,34 @@
|
|||||||
# see settingsDefinition.py
|
# see settingsDefinition.py
|
||||||
|
import logging
|
||||||
import os
|
import os
|
||||||
import os.path
|
import os.path
|
||||||
|
|
||||||
import rendermodes
|
import rendermodes
|
||||||
import util
|
import util
|
||||||
from optimizeimages import Optimizer
|
from optimizeimages import Optimizer
|
||||||
from world import UPPER_LEFT, UPPER_RIGHT, LOWER_LEFT, LOWER_RIGHT
|
from world import LOWER_LEFT, LOWER_RIGHT, UPPER_LEFT, UPPER_RIGHT
|
||||||
import logging
|
|
||||||
|
|
||||||
class ValidationException(Exception):
|
class ValidationException(Exception):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class Setting(object):
|
class Setting(object):
|
||||||
__slots__ = ['required', 'validator', 'default']
|
__slots__ = ['required', 'validator', 'default']
|
||||||
|
|
||||||
def __init__(self, required, validator, default):
|
def __init__(self, required, validator, default):
|
||||||
self.required = required
|
self.required = required
|
||||||
self.validator = validator
|
self.validator = validator
|
||||||
self.default = default
|
self.default = default
|
||||||
|
|
||||||
|
|
||||||
def expand_path(p):
|
def expand_path(p):
|
||||||
p = os.path.expanduser(p)
|
p = os.path.expanduser(p)
|
||||||
p = os.path.expandvars(p)
|
p = os.path.expandvars(p)
|
||||||
p = os.path.abspath(p)
|
p = os.path.abspath(p)
|
||||||
return p
|
return p
|
||||||
|
|
||||||
|
|
||||||
def checkBadEscape(s):
|
def checkBadEscape(s):
|
||||||
# If any of these weird characters are in the path, raise an exception
|
# If any of these weird characters are in the path, raise an exception
|
||||||
# instead of fixing this should help us educate our users about pathslashes
|
# instead of fixing this should help us educate our users about pathslashes
|
||||||
@@ -35,36 +40,42 @@ def checkBadEscape(s):
|
|||||||
"more info." % repr(b))
|
"more info." % repr(b))
|
||||||
for c in range(10):
|
for c in range(10):
|
||||||
if chr(c) in s:
|
if chr(c) in s:
|
||||||
raise ValueError("Invalid character '\\%s' in path. Please use forward slashes ('/'). Please see our docs for more info." % c)
|
raise ValueError("Invalid character '\\%s' in path. "
|
||||||
|
"Please use forward slashes ('/'). "
|
||||||
|
"See our docs for more info." % c)
|
||||||
return s
|
return s
|
||||||
|
|
||||||
|
|
||||||
def validateMarkers(filterlist):
|
def validateMarkers(filterlist):
|
||||||
if type(filterlist) != list:
|
if type(filterlist) != list:
|
||||||
raise ValidationException("Markers must specify a list of filters. This has recently changed, so check the docs.")
|
raise ValidationException("Markers must specify a list of filters.")
|
||||||
for x in filterlist:
|
for x in filterlist:
|
||||||
if type(x) != dict:
|
if type(x) != dict:
|
||||||
raise ValidationException("Markers must specify a list of dictionaries. This has recently changed, so check the docs.")
|
raise ValidationException("Markers must specify a list of dictionaries.")
|
||||||
if "name" not in x:
|
if "name" not in x:
|
||||||
raise ValidationException("Must define a name")
|
raise ValidationException("Filter must define a name.")
|
||||||
if "filterFunction" not in x:
|
if "filterFunction" not in x:
|
||||||
raise ValidationException("Must define a filter function")
|
raise ValidationException("Filter must define a filter function.")
|
||||||
if not callable(x['filterFunction']):
|
if not callable(x['filterFunction']):
|
||||||
raise ValidationException("%r must be a function"% x['filterFunction'])
|
raise ValidationException("%r must be a function." % x['filterFunction'])
|
||||||
return filterlist
|
return filterlist
|
||||||
|
|
||||||
|
|
||||||
def validateOverlays(renderlist):
|
def validateOverlays(renderlist):
|
||||||
if type(renderlist) != list:
|
if type(renderlist) != list:
|
||||||
raise ValidationException("Overlay must specify a list of renders")
|
raise ValidationException("Overlay must specify a list of renders.")
|
||||||
for x in renderlist:
|
for x in renderlist:
|
||||||
if validateStr(x) == '':
|
if validateStr(x) == '':
|
||||||
raise ValidationException("%r must be a string"% x)
|
raise ValidationException("%r must be a string." % x)
|
||||||
return renderlist
|
return renderlist
|
||||||
|
|
||||||
|
|
||||||
def validateWorldPath(worldpath):
|
def validateWorldPath(worldpath):
|
||||||
checkBadEscape(worldpath)
|
checkBadEscape(worldpath)
|
||||||
abs_path = expand_path(worldpath)
|
abs_path = expand_path(worldpath)
|
||||||
if not os.path.exists(os.path.join(abs_path, "level.dat")):
|
if not os.path.exists(os.path.join(abs_path, "level.dat")):
|
||||||
raise ValidationException("No level.dat file in '%s'. Are you sure you have the right path?" % (abs_path,))
|
raise ValidationException("No level.dat file in '%s'. Are you sure you have the right "
|
||||||
|
"path?" % (abs_path,))
|
||||||
return abs_path
|
return abs_path
|
||||||
|
|
||||||
|
|
||||||
@@ -72,65 +83,77 @@ def validateRenderMode(mode):
|
|||||||
# make sure that mode is a list of things that are all rendermode primative
|
# make sure that mode is a list of things that are all rendermode primative
|
||||||
if isinstance(mode, str):
|
if isinstance(mode, str):
|
||||||
# Try and find an item named "mode" in the rendermodes module
|
# Try and find an item named "mode" in the rendermodes module
|
||||||
mode = mode.lower().replace("-","_")
|
mode = mode.lower().replace("-", "_")
|
||||||
try:
|
try:
|
||||||
mode = getattr(rendermodes, mode)
|
mode = getattr(rendermodes, mode)
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
raise ValidationException("You must specify a valid rendermode, not '%s'. See the docs for valid rendermodes." % mode)
|
raise ValidationException("You must specify a valid rendermode, not '%s'. "
|
||||||
|
"See the docs for valid rendermodes." % mode)
|
||||||
|
|
||||||
if isinstance(mode, rendermodes.RenderPrimitive):
|
if isinstance(mode, rendermodes.RenderPrimitive):
|
||||||
mode = [mode]
|
mode = [mode]
|
||||||
|
|
||||||
if not isinstance(mode, list):
|
if not isinstance(mode, list):
|
||||||
raise ValidationException("%r is not a valid list of rendermodes. It should be a list"% mode)
|
raise ValidationException("%r is not a valid list of rendermodes. "
|
||||||
|
"It should be a list." % mode)
|
||||||
|
|
||||||
for m in mode:
|
for m in mode:
|
||||||
if not isinstance(m, rendermodes.RenderPrimitive):
|
if not isinstance(m, rendermodes.RenderPrimitive):
|
||||||
raise ValidationException("%r is not a valid rendermode primitive." % m)
|
raise ValidationException("%r is not a valid rendermode primitive." % m)
|
||||||
|
|
||||||
|
|
||||||
return mode
|
return mode
|
||||||
|
|
||||||
|
|
||||||
def validateNorthDirection(direction):
|
def validateNorthDirection(direction):
|
||||||
# normalize to integers
|
# normalize to integers
|
||||||
intdir = 0 #default
|
intdir = 0 # default
|
||||||
if type(direction) == int:
|
if type(direction) == int:
|
||||||
intdir = direction
|
intdir = direction
|
||||||
elif isinstance(direction, str):
|
elif isinstance(direction, str):
|
||||||
direction = direction.lower().replace("-","").replace("_","")
|
direction = direction.lower().replace("-", "").replace("_", "")
|
||||||
if direction == "upperleft": intdir = UPPER_LEFT
|
if direction == "upperleft":
|
||||||
elif direction == "upperright": intdir = UPPER_RIGHT
|
intdir = UPPER_LEFT
|
||||||
elif direction == "lowerright": intdir = LOWER_RIGHT
|
elif direction == "upperright":
|
||||||
elif direction == "lowerleft": intdir = LOWER_LEFT
|
intdir = UPPER_RIGHT
|
||||||
|
elif direction == "lowerright":
|
||||||
|
intdir = LOWER_RIGHT
|
||||||
|
elif direction == "lowerleft":
|
||||||
|
intdir = LOWER_LEFT
|
||||||
else:
|
else:
|
||||||
raise ValidationException("'%s' is not a valid north direction" % direction)
|
raise ValidationException("'%s' is not a valid north direction." % direction)
|
||||||
if intdir < 0 or intdir > 3:
|
if intdir < 0 or intdir > 3:
|
||||||
raise ValidationException("%r is not a valid north direction" % direction)
|
raise ValidationException("%r is not a valid north direction." % direction)
|
||||||
return intdir
|
return intdir
|
||||||
|
|
||||||
|
|
||||||
def validateRerenderprob(s):
|
def validateRerenderprob(s):
|
||||||
val = float(s)
|
val = float(s)
|
||||||
if val < 0 or val >= 1:
|
if val < 0 or val >= 1:
|
||||||
raise ValidationException("%r is not a valid rerender probability value. Should be between 0.0 and 1.0." % s)
|
raise ValidationException("%r is not a valid rerender probability value. "
|
||||||
|
"Should be between 0.0 and 1.0." % s)
|
||||||
return val
|
return val
|
||||||
|
|
||||||
|
|
||||||
def validateImgFormat(fmt):
|
def validateImgFormat(fmt):
|
||||||
if fmt not in ("png", "jpg", "jpeg", "webp"):
|
if fmt not in ("png", "jpg", "jpeg", "webp"):
|
||||||
raise ValidationException("%r is not a valid image format" % fmt)
|
raise ValidationException("%r is not a valid image format." % fmt)
|
||||||
if fmt == "jpeg": fmt = "jpg"
|
if fmt == "jpeg":
|
||||||
|
fmt = "jpg"
|
||||||
if fmt == "webp":
|
if fmt == "webp":
|
||||||
try:
|
try:
|
||||||
from PIL import _webp
|
from PIL import _webp
|
||||||
except ImportError:
|
except ImportError:
|
||||||
raise ValidationException("WebP is not supported by your PIL/Pillow installation")
|
raise ValidationException("WebP is not supported by your PIL/Pillow installation.")
|
||||||
return fmt
|
return fmt
|
||||||
|
|
||||||
|
|
||||||
def validateImgQuality(qual):
|
def validateImgQuality(qual):
|
||||||
intqual = int(qual)
|
intqual = int(qual)
|
||||||
if (intqual < 0 or intqual > 100):
|
if (intqual < 0 or intqual > 100):
|
||||||
raise ValidationException("%r is not a valid image quality" % intqual)
|
raise ValidationException("%r is not a valid image quality." % intqual)
|
||||||
return intqual
|
return intqual
|
||||||
|
|
||||||
|
|
||||||
def validateBGColor(color):
|
def validateBGColor(color):
|
||||||
"""BG color must be an HTML color, with an option leading # (hash symbol)
|
"""BG color must be an HTML color, with an option leading # (hash symbol)
|
||||||
returns an (r,b,g) 3-tuple
|
returns an (r,b,g) 3-tuple
|
||||||
@@ -139,28 +162,30 @@ def validateBGColor(color):
|
|||||||
if color[0] != "#":
|
if color[0] != "#":
|
||||||
color = "#" + color
|
color = "#" + color
|
||||||
if len(color) != 7:
|
if len(color) != 7:
|
||||||
raise ValidationException("%r is not a valid color. Expected HTML color syntax (i.e. #RRGGBB)" % color)
|
raise ValidationException("%r is not a valid color. Expected HTML color syntax. "
|
||||||
|
"(i.e. #RRGGBB)" % color)
|
||||||
try:
|
try:
|
||||||
r = int(color[1:3], 16)
|
r = int(color[1:3], 16)
|
||||||
g = int(color[3:5], 16)
|
g = int(color[3:5], 16)
|
||||||
b = int(color[5:7], 16)
|
b = int(color[5:7], 16)
|
||||||
return (r,g,b,0)
|
return (r, g, b, 0)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
raise ValidationException("%r is not a valid color. Expected HTML color syntax (i.e. #RRGGBB)" % color)
|
raise ValidationException("%r is not a valid color. Expected HTML color syntax. "
|
||||||
|
"(i.e. #RRGGBB)" % color)
|
||||||
elif type(color) == tuple:
|
elif type(color) == tuple:
|
||||||
if len(color) != 4:
|
if len(color) != 4:
|
||||||
raise ValidationException("%r is not a valid color. Expected a 4-tuple" % (color,))
|
raise ValidationException("%r is not a valid color. Expected a 4-tuple." % (color,))
|
||||||
return color
|
return color
|
||||||
|
|
||||||
|
|
||||||
def validateOptImg(optimizers):
|
def validateOptImg(optimizers):
|
||||||
if isinstance(optimizers, (int, long)):
|
if isinstance(optimizers, (int, long)):
|
||||||
from optimizeimages import pngcrush
|
from optimizeimages import pngcrush
|
||||||
logging.warning("You're using a deprecated definition of optimizeimg. "\
|
logging.warning("You're using a deprecated definition of optimizeimg. "
|
||||||
"We'll do what you say for now, but please fix this as soon as possible.")
|
"We'll do what you say for now, but please fix this as soon as possible.")
|
||||||
optimizers = [pngcrush()]
|
optimizers = [pngcrush()]
|
||||||
if not isinstance(optimizers, list):
|
if not isinstance(optimizers, list):
|
||||||
raise ValidationException("What you passed to optimizeimg is not a list. "\
|
raise ValidationException("What you passed to optimizeimg is not a list. "
|
||||||
"Make sure you specify them like [foo()], with square brackets.")
|
"Make sure you specify them like [foo()], with square brackets.")
|
||||||
|
|
||||||
if optimizers:
|
if optimizers:
|
||||||
@@ -173,34 +198,38 @@ def validateOptImg(optimizers):
|
|||||||
# Check whether the chaining is somewhat sane
|
# Check whether the chaining is somewhat sane
|
||||||
if next_opt:
|
if next_opt:
|
||||||
if opt.is_crusher() and not next_opt.is_crusher():
|
if opt.is_crusher() and not next_opt.is_crusher():
|
||||||
logging.warning("You're feeding a crushed output into an optimizer that does not crush. "\
|
logging.warning("You're feeding a crushed output into an optimizer that "
|
||||||
"This is most likely pointless, and wastes time.")
|
"does not crush. This is most likely pointless, and wastes "
|
||||||
|
"time.")
|
||||||
return optimizers
|
return optimizers
|
||||||
|
|
||||||
|
|
||||||
def validateTexturePath(path):
|
def validateTexturePath(path):
|
||||||
# Expand user dir in directories strings
|
# Expand user dir in directories strings
|
||||||
path = expand_path(path)
|
path = expand_path(path)
|
||||||
if not os.path.exists(path):
|
if not os.path.exists(path):
|
||||||
raise ValidationException("%r does not exist" % path)
|
raise ValidationException("%r does not exist." % path)
|
||||||
return path
|
return path
|
||||||
|
|
||||||
|
|
||||||
def validateBool(b):
|
def validateBool(b):
|
||||||
return bool(b)
|
return bool(b)
|
||||||
|
|
||||||
|
|
||||||
def validateFloat(f):
|
def validateFloat(f):
|
||||||
return float(f)
|
return float(f)
|
||||||
|
|
||||||
|
|
||||||
def validateInt(i):
|
def validateInt(i):
|
||||||
return int(i)
|
return int(i)
|
||||||
|
|
||||||
|
|
||||||
def validateStr(s):
|
def validateStr(s):
|
||||||
return str(s)
|
return str(s)
|
||||||
|
|
||||||
|
|
||||||
def validateDimension(d):
|
def validateDimension(d):
|
||||||
# returns (original, argument to get_type)
|
# returns (original, argument to get_type)
|
||||||
|
|
||||||
# these are provided as arguments to RegionSet.get_type()
|
# these are provided as arguments to RegionSet.get_type()
|
||||||
pretty_names = {
|
pretty_names = {
|
||||||
"nether": "DIM-1",
|
"nether": "DIM-1",
|
||||||
@@ -208,18 +237,19 @@ def validateDimension(d):
|
|||||||
"end": "DIM1",
|
"end": "DIM1",
|
||||||
"default": 0,
|
"default": 0,
|
||||||
}
|
}
|
||||||
|
|
||||||
try:
|
try:
|
||||||
return (d, pretty_names[d])
|
return (d, pretty_names[d])
|
||||||
except KeyError:
|
except KeyError:
|
||||||
return (d, d)
|
return (d, d)
|
||||||
|
|
||||||
|
|
||||||
def validateOutputDir(d):
|
def validateOutputDir(d):
|
||||||
checkBadEscape(d)
|
checkBadEscape(d)
|
||||||
if not d.strip():
|
if not d.strip():
|
||||||
raise ValidationException("You must specify a valid output directory")
|
raise ValidationException("You must specify a valid output directory.")
|
||||||
return expand_path(d)
|
return expand_path(d)
|
||||||
|
|
||||||
|
|
||||||
def validateCrop(value):
|
def validateCrop(value):
|
||||||
if not isinstance(value, list):
|
if not isinstance(value, list):
|
||||||
value = [value]
|
value = [value]
|
||||||
@@ -227,7 +257,8 @@ def validateCrop(value):
|
|||||||
cropZones = []
|
cropZones = []
|
||||||
for zone in value:
|
for zone in value:
|
||||||
if not isinstance(zone, tuple) or len(zone) != 4:
|
if not isinstance(zone, tuple) or len(zone) != 4:
|
||||||
raise ValidationException("The value for the 'crop' setting must be an array of tuples of length 4")
|
raise ValidationException("The value for the 'crop' setting must be an array of "
|
||||||
|
"tuples of length 4.")
|
||||||
a, b, c, d = tuple(int(x) for x in zone)
|
a, b, c, d = tuple(int(x) for x in zone)
|
||||||
|
|
||||||
if a >= c:
|
if a >= c:
|
||||||
@@ -239,17 +270,20 @@ def validateCrop(value):
|
|||||||
|
|
||||||
return cropZones
|
return cropZones
|
||||||
|
|
||||||
|
|
||||||
def validateObserver(observer):
|
def validateObserver(observer):
|
||||||
if all(map(lambda m: hasattr(observer, m), ['start', 'add', 'update', 'finish'])):
|
if all(map(lambda m: hasattr(observer, m), ['start', 'add', 'update', 'finish'])):
|
||||||
return observer
|
return observer
|
||||||
else:
|
else:
|
||||||
raise ValidationException("%r does not look like an observer" % repr(observer))
|
raise ValidationException("%r does not look like an observer." % repr(observer))
|
||||||
|
|
||||||
|
|
||||||
def validateDefaultZoom(z):
|
def validateDefaultZoom(z):
|
||||||
if z > 0:
|
if z > 0:
|
||||||
return int(z)
|
return int(z)
|
||||||
else:
|
else:
|
||||||
raise ValidationException("The default zoom is set below 1")
|
raise ValidationException("The default zoom is set below 1.")
|
||||||
|
|
||||||
|
|
||||||
def validateWebAssetsPath(p):
|
def validateWebAssetsPath(p):
|
||||||
try:
|
try:
|
||||||
@@ -257,18 +291,22 @@ def validateWebAssetsPath(p):
|
|||||||
except ValidationException as e:
|
except ValidationException as e:
|
||||||
raise ValidationException("Bad custom web assets path: %s" % e.message)
|
raise ValidationException("Bad custom web assets path: %s" % e.message)
|
||||||
|
|
||||||
|
|
||||||
def validatePath(p):
|
def validatePath(p):
|
||||||
checkBadEscape(p)
|
checkBadEscape(p)
|
||||||
abs_path = expand_path(p)
|
abs_path = expand_path(p)
|
||||||
if not os.path.exists(abs_path):
|
if not os.path.exists(abs_path):
|
||||||
raise ValidationException("'%s' does not exist. Path initially given as '%s'" % (abs_path,p))
|
raise ValidationException("'%s' does not exist. Path initially given as '%s'"
|
||||||
|
% (abs_path, p))
|
||||||
|
|
||||||
|
|
||||||
def validateManualPOIs(d):
|
def validateManualPOIs(d):
|
||||||
for poi in d:
|
for poi in d:
|
||||||
if not 'x' in poi or not 'y' in poi or not 'z' in poi or not 'id' in poi:
|
if 'x' not in poi or 'y' not in poi or 'z' not in poi or 'id' not in poi:
|
||||||
raise ValidationException("Not all POIs have x/y/z coordinates or an id: %r" % poi)
|
raise ValidationException("Not all POIs have x/y/z coordinates or an id: %r." % poi)
|
||||||
return d
|
return d
|
||||||
|
|
||||||
|
|
||||||
def make_dictValidator(keyvalidator, valuevalidator):
|
def make_dictValidator(keyvalidator, valuevalidator):
|
||||||
"""Compose and return a dict validator -- a validator that validates each
|
"""Compose and return a dict validator -- a validator that validates each
|
||||||
key and value in a dictionary.
|
key and value in a dictionary.
|
||||||
@@ -288,6 +326,7 @@ def make_dictValidator(keyvalidator, valuevalidator):
|
|||||||
v.valuevalidator = valuevalidator
|
v.valuevalidator = valuevalidator
|
||||||
return v
|
return v
|
||||||
|
|
||||||
|
|
||||||
def make_configDictValidator(config, ignore_undefined=False):
|
def make_configDictValidator(config, ignore_undefined=False):
|
||||||
"""Okay, stay with me here, this may get confusing. This function returns a
|
"""Okay, stay with me here, this may get confusing. This function returns a
|
||||||
validator that validates a "configdict". This is a term I just made up to
|
validator that validates a "configdict". This is a term I just made up to
|
||||||
@@ -326,10 +365,9 @@ def make_configDictValidator(config, ignore_undefined=False):
|
|||||||
newdict[key] = d[key]
|
newdict[key] = d[key]
|
||||||
elif match:
|
elif match:
|
||||||
raise ValidationException(
|
raise ValidationException(
|
||||||
"'%s' is not a configuration item. Did you mean '%s'?"
|
"'%s' is not a configuration item. Did you mean '%s'?" % (key, match))
|
||||||
% (key, match))
|
|
||||||
elif not ignore_undefined:
|
elif not ignore_undefined:
|
||||||
raise ValidationException("'%s' is not a configuration item" % key)
|
raise ValidationException("'%s' is not a configuration item." % key)
|
||||||
else:
|
else:
|
||||||
# the key is to be ignored. Copy it as-is to the `newdict`
|
# the key is to be ignored. Copy it as-is to the `newdict`
|
||||||
# to be returned. It shouldn't conflict, and may be used as
|
# to be returned. It shouldn't conflict, and may be used as
|
||||||
@@ -350,13 +388,12 @@ def make_configDictValidator(config, ignore_undefined=False):
|
|||||||
# The user did not give us this key, there is no default, AND
|
# The user did not give us this key, there is no default, AND
|
||||||
# it's required. This is an error.
|
# it's required. This is an error.
|
||||||
if configkey in undefined_key_matches:
|
if configkey in undefined_key_matches:
|
||||||
raise ValidationException("Key '%s' is not a valid "
|
raise ValidationException(
|
||||||
"configuration item. Did you mean '%s'?"
|
"Key '%s' is not a valid configuration item. Did you mean '%s'?"
|
||||||
% (undefined_key_matches[configkey], configkey))
|
% (undefined_key_matches[configkey], configkey))
|
||||||
else:
|
else:
|
||||||
raise ValidationException("Required key '%s' was not "
|
raise ValidationException("Required key '%s' was not specified. You must give "
|
||||||
"specified. You must give a value for this setting"
|
"a value for this setting." % configkey)
|
||||||
% configkey)
|
|
||||||
|
|
||||||
return newdict
|
return newdict
|
||||||
# Put these objects as attributes of the function so they can be accessed
|
# Put these objects as attributes of the function so they can be accessed
|
||||||
@@ -365,11 +402,13 @@ def make_configDictValidator(config, ignore_undefined=False):
|
|||||||
configDictValidator.ignore_undefined = ignore_undefined
|
configDictValidator.ignore_undefined = ignore_undefined
|
||||||
return configDictValidator
|
return configDictValidator
|
||||||
|
|
||||||
|
|
||||||
def error(errstr):
|
def error(errstr):
|
||||||
def validator(_):
|
def validator(_):
|
||||||
raise ValidationException(errstr)
|
raise ValidationException(errstr)
|
||||||
return validator
|
return validator
|
||||||
|
|
||||||
|
|
||||||
# Activestate recipe 576874
|
# Activestate recipe 576874
|
||||||
def _levenshtein(s1, s2):
|
def _levenshtein(s1, s2):
|
||||||
l1 = len(s1)
|
l1 = len(s1)
|
||||||
@@ -377,15 +416,18 @@ def _levenshtein(s1, s2):
|
|||||||
|
|
||||||
matrix = [range(l1 + 1)] * (l2 + 1)
|
matrix = [range(l1 + 1)] * (l2 + 1)
|
||||||
for zz in range(l2 + 1):
|
for zz in range(l2 + 1):
|
||||||
matrix[zz] = range(zz,zz + l1 + 1)
|
matrix[zz] = range(zz, zz + l1 + 1)
|
||||||
for zz in range(0,l2):
|
for zz in range(0, l2):
|
||||||
for sz in range(0,l1):
|
for sz in range(0, l1):
|
||||||
if s1[sz] == s2[zz]:
|
if s1[sz] == s2[zz]:
|
||||||
matrix[zz+1][sz+1] = min(matrix[zz+1][sz] + 1, matrix[zz][sz+1] + 1, matrix[zz][sz])
|
matrix[zz + 1][sz + 1] = min(matrix[zz + 1][sz] + 1, matrix[zz][sz + 1] + 1,
|
||||||
|
matrix[zz][sz])
|
||||||
else:
|
else:
|
||||||
matrix[zz+1][sz+1] = min(matrix[zz+1][sz] + 1, matrix[zz][sz+1] + 1, matrix[zz][sz] + 1)
|
matrix[zz + 1][sz + 1] = min(matrix[zz + 1][sz] + 1, matrix[zz][sz + 1] + 1,
|
||||||
|
matrix[zz][sz] + 1)
|
||||||
return matrix[l2][l1]
|
return matrix[l2][l1]
|
||||||
|
|
||||||
|
|
||||||
def _get_closest_match(s, keys):
|
def _get_closest_match(s, keys):
|
||||||
"""Returns a probable match for the given key `s` out of the possible keys in
|
"""Returns a probable match for the given key `s` out of the possible keys in
|
||||||
`keys`. Returns None if no matches are very close.
|
`keys`. Returns None if no matches are very close.
|
||||||
@@ -396,7 +438,7 @@ def _get_closest_match(s, keys):
|
|||||||
threshold = 3
|
threshold = 3
|
||||||
|
|
||||||
minmatch = None
|
minmatch = None
|
||||||
mindist = threshold+1
|
mindist = threshold + 1
|
||||||
|
|
||||||
for key in keys:
|
for key in keys:
|
||||||
d = _levenshtein(s, key)
|
d = _levenshtein(s, key)
|
||||||
|
|||||||
Reference in New Issue
Block a user