1import unittest
2from unittest.mock import patch
3import builtins
4import rlcompleter
5
6class CompleteMe:
7    """ Trivial class used in testing rlcompleter.Completer. """
8    spam = 1
9    _ham = 2
10
11
12class TestRlcompleter(unittest.TestCase):
13    def setUp(self):
14        self.stdcompleter = rlcompleter.Completer()
15        self.completer = rlcompleter.Completer(dict(spam=int,
16                                                    egg=str,
17                                                    CompleteMe=CompleteMe))
18
19        # forces stdcompleter to bind builtins namespace
20        self.stdcompleter.complete('', 0)
21
22    def test_namespace(self):
23        class A(dict):
24            pass
25        class B(list):
26            pass
27
28        self.assertTrue(self.stdcompleter.use_main_ns)
29        self.assertFalse(self.completer.use_main_ns)
30        self.assertFalse(rlcompleter.Completer(A()).use_main_ns)
31        self.assertRaises(TypeError, rlcompleter.Completer, B((1,)))
32
33    def test_global_matches(self):
34        # test with builtins namespace
35        self.assertEqual(sorted(self.stdcompleter.global_matches('di')),
36                         [x+'(' for x in dir(builtins) if x.startswith('di')])
37        self.assertEqual(sorted(self.stdcompleter.global_matches('st')),
38                         [x+'(' for x in dir(builtins) if x.startswith('st')])
39        self.assertEqual(self.stdcompleter.global_matches('akaksajadhak'), [])
40
41        # test with a customized namespace
42        self.assertEqual(self.completer.global_matches('CompleteM'),
43                         ['CompleteMe()'])
44        self.assertEqual(self.completer.global_matches('eg'),
45                         ['egg('])
46        # XXX: see issue5256
47        self.assertEqual(self.completer.global_matches('CompleteM'),
48                         ['CompleteMe()'])
49
50    def test_attr_matches(self):
51        # test with builtins namespace
52        self.assertEqual(self.stdcompleter.attr_matches('str.s'),
53                         ['str.{}('.format(x) for x in dir(str)
54                          if x.startswith('s')])
55        self.assertEqual(self.stdcompleter.attr_matches('tuple.foospamegg'), [])
56        expected = sorted({'None.%s%s' % (x, '(' if x != '__doc__' else '')
57                           for x in dir(None)})
58        self.assertEqual(self.stdcompleter.attr_matches('None.'), expected)
59        self.assertEqual(self.stdcompleter.attr_matches('None._'), expected)
60        self.assertEqual(self.stdcompleter.attr_matches('None.__'), expected)
61
62        # test with a customized namespace
63        self.assertEqual(self.completer.attr_matches('CompleteMe.sp'),
64                         ['CompleteMe.spam'])
65        self.assertEqual(self.completer.attr_matches('Completeme.egg'), [])
66        self.assertEqual(self.completer.attr_matches('CompleteMe.'),
67                         ['CompleteMe.mro()', 'CompleteMe.spam'])
68        self.assertEqual(self.completer.attr_matches('CompleteMe._'),
69                         ['CompleteMe._ham'])
70        matches = self.completer.attr_matches('CompleteMe.__')
71        for x in matches:
72            self.assertTrue(x.startswith('CompleteMe.__'), x)
73        self.assertIn('CompleteMe.__name__', matches)
74        self.assertIn('CompleteMe.__new__(', matches)
75
76        with patch.object(CompleteMe, "me", CompleteMe, create=True):
77            self.assertEqual(self.completer.attr_matches('CompleteMe.me.me.sp'),
78                             ['CompleteMe.me.me.spam'])
79            self.assertEqual(self.completer.attr_matches('egg.s'),
80                             ['egg.{}('.format(x) for x in dir(str)
81                              if x.startswith('s')])
82
83    def test_excessive_getattr(self):
84        """Ensure getattr() is invoked no more than once per attribute"""
85
86        # note the special case for @property methods below; that is why
87        # we use __dir__ and __getattr__ in class Foo to create a "magic"
88        # class attribute 'bar'. This forces `getattr` to call __getattr__
89        # (which is doesn't necessarily do).
90        class Foo:
91            calls = 0
92            bar = ''
93            def __getattribute__(self, name):
94                if name == 'bar':
95                    self.calls += 1
96                    return None
97                return super().__getattribute__(name)
98
99        f = Foo()
100        completer = rlcompleter.Completer(dict(f=f))
101        self.assertEqual(completer.complete('f.b', 0), 'f.bar')
102        self.assertEqual(f.calls, 1)
103
104    def test_property_method_not_called(self):
105        class Foo:
106            _bar = 0
107            property_called = False
108
109            @property
110            def bar(self):
111                self.property_called = True
112                return self._bar
113
114        f = Foo()
115        completer = rlcompleter.Completer(dict(f=f))
116        self.assertEqual(completer.complete('f.b', 0), 'f.bar')
117        self.assertFalse(f.property_called)
118
119
120    def test_uncreated_attr(self):
121        # Attributes like properties and slots should be completed even when
122        # they haven't been created on an instance
123        class Foo:
124            __slots__ = ("bar",)
125        completer = rlcompleter.Completer(dict(f=Foo()))
126        self.assertEqual(completer.complete('f.', 0), 'f.bar')
127
128    @unittest.mock.patch('rlcompleter._readline_available', False)
129    def test_complete(self):
130        completer = rlcompleter.Completer()
131        self.assertEqual(completer.complete('', 0), '\t')
132        self.assertEqual(completer.complete('a', 0), 'and ')
133        self.assertEqual(completer.complete('a', 1), 'as ')
134        self.assertEqual(completer.complete('as', 2), 'assert ')
135        self.assertEqual(completer.complete('an', 0), 'and ')
136        self.assertEqual(completer.complete('pa', 0), 'pass')
137        self.assertEqual(completer.complete('Fa', 0), 'False')
138        self.assertEqual(completer.complete('el', 0), 'elif ')
139        self.assertEqual(completer.complete('el', 1), 'else')
140        self.assertEqual(completer.complete('tr', 0), 'try:')
141        self.assertEqual(completer.complete('_', 0), '_')
142        self.assertEqual(completer.complete('match', 0), 'match ')
143        self.assertEqual(completer.complete('case', 0), 'case ')
144
145    def test_duplicate_globals(self):
146        namespace = {
147            'False': None,  # Keyword vs builtin vs namespace
148            'assert': None,  # Keyword vs namespace
149            'try': lambda: None,  # Keyword vs callable
150            'memoryview': None,  # Callable builtin vs non-callable
151            'Ellipsis': lambda: None,  # Non-callable builtin vs callable
152        }
153        completer = rlcompleter.Completer(namespace)
154        self.assertEqual(completer.complete('False', 0), 'False')
155        self.assertIsNone(completer.complete('False', 1))  # No duplicates
156        # Space or colon added due to being a reserved keyword
157        self.assertEqual(completer.complete('assert', 0), 'assert ')
158        self.assertIsNone(completer.complete('assert', 1))
159        self.assertEqual(completer.complete('try', 0), 'try:')
160        self.assertIsNone(completer.complete('try', 1))
161        # No opening bracket "(" because we overrode the built-in class
162        self.assertEqual(completer.complete('memoryview', 0), 'memoryview')
163        self.assertIsNone(completer.complete('memoryview', 1))
164        self.assertEqual(completer.complete('Ellipsis', 0), 'Ellipsis()')
165        self.assertIsNone(completer.complete('Ellipsis', 1))
166
167if __name__ == '__main__':
168    unittest.main()
169