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
This commit is contained in:
Madhusudhan Kasula 2020-05-20 12:49:49 +05:30
parent e2a4f87879
commit 7a58345743
3 changed files with 48 additions and 3 deletions

View File

@ -5,6 +5,7 @@
:synopsis: Python identifier completion, suitable for the GNU readline library.
.. sectionauthor:: Moshe Zadka <moshez@zadka.site.co.il>
.. sectionauthor:: Madhusudhan Kasula <kasula.madhusudhan@gmail.com>
**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.

View File

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

View File

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