config: Steal did you mean code from settings.

This commit is contained in:
Thomas Adamcik 2013-04-05 23:29:45 +02:00
parent c9115aa480
commit bd3d8f6932
3 changed files with 55 additions and 24 deletions

View File

@ -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)

View File

@ -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)

View File

@ -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)