config: Steal did you mean code from settings.
This commit is contained in:
parent
c9115aa480
commit
bd3d8f6932
@ -33,6 +33,39 @@ def validate_maximum(value, maximum):
|
||||
raise ValueError('%r must be smaller than %r.' % (value, maximum))
|
||||
|
||||
|
||||
# TODO: move this and levenshtein to a more appropriate class.
|
||||
def did_you_mean(name, choices):
|
||||
"""Suggest most likely setting based on levenshtein."""
|
||||
if not choices:
|
||||
return None
|
||||
|
||||
name = name.lower()
|
||||
candidates = [(levenshtein(name, c), c) for c in choices]
|
||||
candidates.sort()
|
||||
|
||||
if candidates[0][0] <= 3:
|
||||
return candidates[0][1]
|
||||
return None
|
||||
|
||||
|
||||
def levenshtein(a, b):
|
||||
"""Calculates the Levenshtein distance between a and b."""
|
||||
n, m = len(a), len(b)
|
||||
if n > m:
|
||||
return levenshtein(b, a)
|
||||
|
||||
current = xrange(n + 1)
|
||||
for i in xrange(1, m + 1):
|
||||
previous, current = current, [i] + [0] * n
|
||||
for j in xrange(1, n + 1):
|
||||
add, delete = previous[j] + 1, current[j - 1] + 1
|
||||
change = previous[j - 1]
|
||||
if a[j - 1] != b[i - 1]:
|
||||
change += 1
|
||||
current[j] = min(add, delete, change)
|
||||
return current[n]
|
||||
|
||||
|
||||
class ConfigValue(object):
|
||||
"""Represents a config key's value and how to handle it.
|
||||
|
||||
@ -248,6 +281,9 @@ class ConfigSchema(object):
|
||||
values[key] = self._schema[key].deserialize(value)
|
||||
except KeyError: # not in our schema
|
||||
errors[key] = 'unknown config key.'
|
||||
suggestion = did_you_mean(key, self._schema.keys())
|
||||
if suggestion:
|
||||
errors[key] += ' Did you mean %s?' % suggestion
|
||||
except ValueError as e: # deserialization failed
|
||||
errors[key] = str(e)
|
||||
|
||||
|
||||
@ -395,3 +395,22 @@ class LogLevelConfigSchemaTest(unittest.TestCase):
|
||||
result = schema.format('levels', {'foo.bar': logging.DEBUG, 'baz': logging.INFO})
|
||||
self.assertEqual('\n'.join(expected), result)
|
||||
|
||||
|
||||
class DidYouMeanTest(unittest.TestCase):
|
||||
def testSuggestoins(self):
|
||||
choices = ('enabled', 'username', 'password', 'bitrate', 'timeout')
|
||||
|
||||
suggestion = config.did_you_mean('bitrate', choices)
|
||||
self.assertEqual(suggestion, 'bitrate')
|
||||
|
||||
suggestion = config.did_you_mean('bitrote', choices)
|
||||
self.assertEqual(suggestion, 'bitrate')
|
||||
|
||||
suggestion = config.did_you_mean('Bitrot', choices)
|
||||
self.assertEqual(suggestion, 'bitrate')
|
||||
|
||||
suggestion = config.did_you_mean('BTROT', choices)
|
||||
self.assertEqual(suggestion, 'bitrate')
|
||||
|
||||
suggestion = config.did_you_mean('btro', choices)
|
||||
self.assertEqual(suggestion, None)
|
||||
|
||||
@ -149,27 +149,3 @@ class SettingsProxyTest(unittest.TestCase):
|
||||
def test_value_ending_in_path_can_be_none(self):
|
||||
self.settings.TEST_PATH = None
|
||||
self.assertEqual(self.settings.TEST_PATH, None)
|
||||
|
||||
|
||||
class DidYouMeanTest(unittest.TestCase):
|
||||
def testSuggestoins(self):
|
||||
defaults = {
|
||||
'MPD_SERVER_HOSTNAME': '::',
|
||||
'MPD_SERVER_PORT': 6600,
|
||||
'SPOTIFY_BITRATE': 160,
|
||||
}
|
||||
|
||||
suggestion = setting_utils.did_you_mean('spotify_bitrate', defaults)
|
||||
self.assertEqual(suggestion, 'SPOTIFY_BITRATE')
|
||||
|
||||
suggestion = setting_utils.did_you_mean('SPOTIFY_BITROTE', defaults)
|
||||
self.assertEqual(suggestion, 'SPOTIFY_BITRATE')
|
||||
|
||||
suggestion = setting_utils.did_you_mean('SPITIFY_BITROT', defaults)
|
||||
self.assertEqual(suggestion, 'SPOTIFY_BITRATE')
|
||||
|
||||
suggestion = setting_utils.did_you_mean('SPTIFY_BITROT', defaults)
|
||||
self.assertEqual(suggestion, 'SPOTIFY_BITRATE')
|
||||
|
||||
suggestion = setting_utils.did_you_mean('SPTIFY_BITRO', defaults)
|
||||
self.assertEqual(suggestion, None)
|
||||
|
||||
Loading…
Reference in New Issue
Block a user