From 7a583457434b4485c91ad7b13923cce02e9c6cc1 Mon Sep 17 00:00:00 2001 From: Madhusudhan Kasula Date: Wed, 20 May 2020 12:49:49 +0530 Subject: [PATCH] bpo-40529: rlcompleter with case insensitive Incorporated review comments @ Lib/rlcompleter.py Added documentation @ Doc/library/rlcompleter.rst Added unit testcase test_case_insentive_matches() @ Lib/test/test_rlcompleter.py * skip news --- Doc/library/rlcompleter.rst | 17 +++++++++++++++++ Lib/rlcompleter.py | 8 +++++--- Lib/test/test_rlcompleter.py | 26 ++++++++++++++++++++++++++ 3 files changed, 48 insertions(+), 3 deletions(-) diff --git a/Doc/library/rlcompleter.rst b/Doc/library/rlcompleter.rst index 40b09ce8978..9662ab965ad 100644 --- a/Doc/library/rlcompleter.rst +++ b/Doc/library/rlcompleter.rst @@ -5,6 +5,7 @@ :synopsis: Python identifier completion, suitable for the GNU readline library. .. sectionauthor:: Moshe Zadka +.. sectionauthor:: Madhusudhan Kasula **Source code:** :source:`Lib/rlcompleter.py` @@ -59,3 +60,19 @@ Completer objects have the following method: :func:`dir` function. Any exception raised during the evaluation of the expression is caught, silenced and :const:`None` is returned. + +.. _case-sensitivity: + +Case Sensitivity +----------------- + +You can change the Completer's default case sensitive selection to case insensitive using the following method: + + +.. function:: rlcompleter.set_case_insensitive(option) + + Return the *None*. + + If called with *True*, it will set rlcompleter for case insensitive completions. + + If called with *False*, it will set to default case sensitive completions. diff --git a/Lib/rlcompleter.py b/Lib/rlcompleter.py index 12c7d25c951..47ac6579d76 100644 --- a/Lib/rlcompleter.py +++ b/Lib/rlcompleter.py @@ -110,12 +110,13 @@ class Completer: defined in self.namespace that match. """ + import re import keyword matches = [] seen = {"__builtins__"} n = len(text) for word in keyword.kwlist: - if word[:n] == text: + if re.match(text, word[:n], flags=_re_ignorecase_flags): seen.add(word) if word in {'finally', 'try'}: word = word + ':' @@ -126,7 +127,8 @@ class Completer: matches.append(word) for nspace in [self.namespace, builtins.__dict__]: for word, val in nspace.items(): - if word[:n] == text and word not in seen: + if (re.match(text, word[:n], flags=_re_ignorecase_flags) and + word not in seen): seen.add(word) matches.append(self._callable_postfix(val, word)) return matches @@ -193,7 +195,7 @@ _re_ignorecase_flags = 0 def set_case_insensitive(option): import re global _re_ignorecase_flags - _re_ignorecase_flags = option and re.IGNORECASE or 0 + _re_ignorecase_flags = re.IGNORECASE if option else 0 def get_class_members(klass): ret = dir(klass) diff --git a/Lib/test/test_rlcompleter.py b/Lib/test/test_rlcompleter.py index 0dc1080ca32..a4c1c306d21 100644 --- a/Lib/test/test_rlcompleter.py +++ b/Lib/test/test_rlcompleter.py @@ -137,5 +137,31 @@ class TestRlcompleter(unittest.TestCase): self.assertEqual(completer.complete('Ellipsis', 0), 'Ellipsis(') self.assertIsNone(completer.complete('Ellipsis', 1)) + def test_case_insentive_matches(self): + # add an additional attr for testing + CompleteMe.SpAm = 2 + # enable the case insensitive option + rlcompleter.set_case_insensitive(True) + # test globals + self.assertEqual(self.completer.global_matches('completem'), + ['CompleteMe(']) + # test attr + self.assertNotEqual(self.completer.attr_matches('CompleteMe.spa'), + ['CompleteMe.spam', 'CompleteMe.SpAm']) + # disable the case insensitive option + rlcompleter.set_case_insensitive(False) + # test globals + self.assertNotEqual(self.completer.global_matches('completem'), + ['CompleteMe(']) + self.assertEqual(self.completer.global_matches('CompleteM'), + ['CompleteMe(']) + # test attr + self.assertNotEqual(self.completer.attr_matches('CompleteMe.spa'), + ['CompleteMe.spam', 'CompleteMe.SpAm']) + self.assertEqual(self.completer.attr_matches('CompleteMe.sp'), + ['CompleteMe.spam']) + # delete the additional attr + del(CompleteMe.SpAm) + if __name__ == '__main__': unittest.main()