From bd3d8f693201f7018a2e62c2eaeb469f57e9aa93 Mon Sep 17 00:00:00 2001 From: Thomas Adamcik Date: Fri, 5 Apr 2013 23:29:45 +0200 Subject: [PATCH 1/2] config: Steal did you mean code from settings. --- mopidy/utils/config.py | 36 ++++++++++++++++++++++++++++++++++++ tests/utils/config_test.py | 19 +++++++++++++++++++ tests/utils/settings_test.py | 24 ------------------------ 3 files changed, 55 insertions(+), 24 deletions(-) diff --git a/mopidy/utils/config.py b/mopidy/utils/config.py index 1a3127b5..aa1b06fd 100644 --- a/mopidy/utils/config.py +++ b/mopidy/utils/config.py @@ -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) diff --git a/tests/utils/config_test.py b/tests/utils/config_test.py index 77c846df..ad86b961 100644 --- a/tests/utils/config_test.py +++ b/tests/utils/config_test.py @@ -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) diff --git a/tests/utils/settings_test.py b/tests/utils/settings_test.py index 3b1e67b0..3aa595e3 100644 --- a/tests/utils/settings_test.py +++ b/tests/utils/settings_test.py @@ -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) From 5a79b65d47abad90e80d6eb37a3e581d98f727e9 Mon Sep 17 00:00:00 2001 From: Thomas Adamcik Date: Fri, 5 Apr 2013 23:31:31 +0200 Subject: [PATCH 2/2] settings: Remove did you mean. --- mopidy/utils/settings.py | 36 ------------------------------------ tests/utils/settings_test.py | 3 +-- 2 files changed, 1 insertion(+), 38 deletions(-) diff --git a/mopidy/utils/settings.py b/mopidy/utils/settings.py index 5916ee24..f903a70d 100644 --- a/mopidy/utils/settings.py +++ b/mopidy/utils/settings.py @@ -169,41 +169,5 @@ def validate_settings(defaults, settings): elif setting not in defaults and not setting.startswith('CUSTOM_'): errors[setting] = 'Unknown setting.' - suggestion = did_you_mean(setting, defaults) - - if suggestion: - errors[setting] += ' Did you mean %s?' % suggestion return errors - - -def did_you_mean(setting, defaults): - """Suggest most likely setting based on levenshtein.""" - if not defaults: - return None - - setting = setting.upper() - candidates = [(levenshtein(setting, d), d) for d in defaults] - 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] diff --git a/tests/utils/settings_test.py b/tests/utils/settings_test.py index 3aa595e3..ce763486 100644 --- a/tests/utils/settings_test.py +++ b/tests/utils/settings_test.py @@ -24,8 +24,7 @@ class ValidateSettingsTest(unittest.TestCase): result = setting_utils.validate_settings( self.defaults, {'MPD_SERVER_HOSTNMAE': '127.0.0.1'}) self.assertEqual( - result['MPD_SERVER_HOSTNMAE'], - 'Unknown setting. Did you mean MPD_SERVER_HOSTNAME?') + result['MPD_SERVER_HOSTNMAE'], 'Unknown setting.') def test_custom_settings_does_not_return_errors(self): result = setting_utils.validate_settings(