xref: /aosp_15_r20/prebuilts/build-tools/common/py3-stdlib/rlcompleter.py (revision cda5da8d549138a6648c5ee6d7a49cf8f4a657be)
1*cda5da8dSAndroid Build Coastguard Worker"""Word completion for GNU readline.
2*cda5da8dSAndroid Build Coastguard Worker
3*cda5da8dSAndroid Build Coastguard WorkerThe completer completes keywords, built-ins and globals in a selectable
4*cda5da8dSAndroid Build Coastguard Workernamespace (which defaults to __main__); when completing NAME.NAME..., it
5*cda5da8dSAndroid Build Coastguard Workerevaluates (!) the expression up to the last dot and completes its attributes.
6*cda5da8dSAndroid Build Coastguard Worker
7*cda5da8dSAndroid Build Coastguard WorkerIt's very cool to do "import sys" type "sys.", hit the completion key (twice),
8*cda5da8dSAndroid Build Coastguard Workerand see the list of names defined by the sys module!
9*cda5da8dSAndroid Build Coastguard Worker
10*cda5da8dSAndroid Build Coastguard WorkerTip: to use the tab key as the completion key, call
11*cda5da8dSAndroid Build Coastguard Worker
12*cda5da8dSAndroid Build Coastguard Worker    readline.parse_and_bind("tab: complete")
13*cda5da8dSAndroid Build Coastguard Worker
14*cda5da8dSAndroid Build Coastguard WorkerNotes:
15*cda5da8dSAndroid Build Coastguard Worker
16*cda5da8dSAndroid Build Coastguard Worker- Exceptions raised by the completer function are *ignored* (and generally cause
17*cda5da8dSAndroid Build Coastguard Worker  the completion to fail).  This is a feature -- since readline sets the tty
18*cda5da8dSAndroid Build Coastguard Worker  device in raw (or cbreak) mode, printing a traceback wouldn't work well
19*cda5da8dSAndroid Build Coastguard Worker  without some complicated hoopla to save, reset and restore the tty state.
20*cda5da8dSAndroid Build Coastguard Worker
21*cda5da8dSAndroid Build Coastguard Worker- The evaluation of the NAME.NAME... form may cause arbitrary application
22*cda5da8dSAndroid Build Coastguard Worker  defined code to be executed if an object with a __getattr__ hook is found.
23*cda5da8dSAndroid Build Coastguard Worker  Since it is the responsibility of the application (or the user) to enable this
24*cda5da8dSAndroid Build Coastguard Worker  feature, I consider this an acceptable risk.  More complicated expressions
25*cda5da8dSAndroid Build Coastguard Worker  (e.g. function calls or indexing operations) are *not* evaluated.
26*cda5da8dSAndroid Build Coastguard Worker
27*cda5da8dSAndroid Build Coastguard Worker- When the original stdin is not a tty device, GNU readline is never
28*cda5da8dSAndroid Build Coastguard Worker  used, and this module (and the readline module) are silently inactive.
29*cda5da8dSAndroid Build Coastguard Worker
30*cda5da8dSAndroid Build Coastguard Worker"""
31*cda5da8dSAndroid Build Coastguard Worker
32*cda5da8dSAndroid Build Coastguard Workerimport atexit
33*cda5da8dSAndroid Build Coastguard Workerimport builtins
34*cda5da8dSAndroid Build Coastguard Workerimport inspect
35*cda5da8dSAndroid Build Coastguard Workerimport keyword
36*cda5da8dSAndroid Build Coastguard Workerimport re
37*cda5da8dSAndroid Build Coastguard Workerimport __main__
38*cda5da8dSAndroid Build Coastguard Worker
39*cda5da8dSAndroid Build Coastguard Worker__all__ = ["Completer"]
40*cda5da8dSAndroid Build Coastguard Worker
41*cda5da8dSAndroid Build Coastguard Workerclass Completer:
42*cda5da8dSAndroid Build Coastguard Worker    def __init__(self, namespace = None):
43*cda5da8dSAndroid Build Coastguard Worker        """Create a new completer for the command line.
44*cda5da8dSAndroid Build Coastguard Worker
45*cda5da8dSAndroid Build Coastguard Worker        Completer([namespace]) -> completer instance.
46*cda5da8dSAndroid Build Coastguard Worker
47*cda5da8dSAndroid Build Coastguard Worker        If unspecified, the default namespace where completions are performed
48*cda5da8dSAndroid Build Coastguard Worker        is __main__ (technically, __main__.__dict__). Namespaces should be
49*cda5da8dSAndroid Build Coastguard Worker        given as dictionaries.
50*cda5da8dSAndroid Build Coastguard Worker
51*cda5da8dSAndroid Build Coastguard Worker        Completer instances should be used as the completion mechanism of
52*cda5da8dSAndroid Build Coastguard Worker        readline via the set_completer() call:
53*cda5da8dSAndroid Build Coastguard Worker
54*cda5da8dSAndroid Build Coastguard Worker        readline.set_completer(Completer(my_namespace).complete)
55*cda5da8dSAndroid Build Coastguard Worker        """
56*cda5da8dSAndroid Build Coastguard Worker
57*cda5da8dSAndroid Build Coastguard Worker        if namespace and not isinstance(namespace, dict):
58*cda5da8dSAndroid Build Coastguard Worker            raise TypeError('namespace must be a dictionary')
59*cda5da8dSAndroid Build Coastguard Worker
60*cda5da8dSAndroid Build Coastguard Worker        # Don't bind to namespace quite yet, but flag whether the user wants a
61*cda5da8dSAndroid Build Coastguard Worker        # specific namespace or to use __main__.__dict__. This will allow us
62*cda5da8dSAndroid Build Coastguard Worker        # to bind to __main__.__dict__ at completion time, not now.
63*cda5da8dSAndroid Build Coastguard Worker        if namespace is None:
64*cda5da8dSAndroid Build Coastguard Worker            self.use_main_ns = 1
65*cda5da8dSAndroid Build Coastguard Worker        else:
66*cda5da8dSAndroid Build Coastguard Worker            self.use_main_ns = 0
67*cda5da8dSAndroid Build Coastguard Worker            self.namespace = namespace
68*cda5da8dSAndroid Build Coastguard Worker
69*cda5da8dSAndroid Build Coastguard Worker    def complete(self, text, state):
70*cda5da8dSAndroid Build Coastguard Worker        """Return the next possible completion for 'text'.
71*cda5da8dSAndroid Build Coastguard Worker
72*cda5da8dSAndroid Build Coastguard Worker        This is called successively with state == 0, 1, 2, ... until it
73*cda5da8dSAndroid Build Coastguard Worker        returns None.  The completion should begin with 'text'.
74*cda5da8dSAndroid Build Coastguard Worker
75*cda5da8dSAndroid Build Coastguard Worker        """
76*cda5da8dSAndroid Build Coastguard Worker        if self.use_main_ns:
77*cda5da8dSAndroid Build Coastguard Worker            self.namespace = __main__.__dict__
78*cda5da8dSAndroid Build Coastguard Worker
79*cda5da8dSAndroid Build Coastguard Worker        if not text.strip():
80*cda5da8dSAndroid Build Coastguard Worker            if state == 0:
81*cda5da8dSAndroid Build Coastguard Worker                if _readline_available:
82*cda5da8dSAndroid Build Coastguard Worker                    readline.insert_text('\t')
83*cda5da8dSAndroid Build Coastguard Worker                    readline.redisplay()
84*cda5da8dSAndroid Build Coastguard Worker                    return ''
85*cda5da8dSAndroid Build Coastguard Worker                else:
86*cda5da8dSAndroid Build Coastguard Worker                    return '\t'
87*cda5da8dSAndroid Build Coastguard Worker            else:
88*cda5da8dSAndroid Build Coastguard Worker                return None
89*cda5da8dSAndroid Build Coastguard Worker
90*cda5da8dSAndroid Build Coastguard Worker        if state == 0:
91*cda5da8dSAndroid Build Coastguard Worker            if "." in text:
92*cda5da8dSAndroid Build Coastguard Worker                self.matches = self.attr_matches(text)
93*cda5da8dSAndroid Build Coastguard Worker            else:
94*cda5da8dSAndroid Build Coastguard Worker                self.matches = self.global_matches(text)
95*cda5da8dSAndroid Build Coastguard Worker        try:
96*cda5da8dSAndroid Build Coastguard Worker            return self.matches[state]
97*cda5da8dSAndroid Build Coastguard Worker        except IndexError:
98*cda5da8dSAndroid Build Coastguard Worker            return None
99*cda5da8dSAndroid Build Coastguard Worker
100*cda5da8dSAndroid Build Coastguard Worker    def _callable_postfix(self, val, word):
101*cda5da8dSAndroid Build Coastguard Worker        if callable(val):
102*cda5da8dSAndroid Build Coastguard Worker            word += "("
103*cda5da8dSAndroid Build Coastguard Worker            try:
104*cda5da8dSAndroid Build Coastguard Worker                if not inspect.signature(val).parameters:
105*cda5da8dSAndroid Build Coastguard Worker                    word += ")"
106*cda5da8dSAndroid Build Coastguard Worker            except ValueError:
107*cda5da8dSAndroid Build Coastguard Worker                pass
108*cda5da8dSAndroid Build Coastguard Worker
109*cda5da8dSAndroid Build Coastguard Worker        return word
110*cda5da8dSAndroid Build Coastguard Worker
111*cda5da8dSAndroid Build Coastguard Worker    def global_matches(self, text):
112*cda5da8dSAndroid Build Coastguard Worker        """Compute matches when text is a simple name.
113*cda5da8dSAndroid Build Coastguard Worker
114*cda5da8dSAndroid Build Coastguard Worker        Return a list of all keywords, built-in functions and names currently
115*cda5da8dSAndroid Build Coastguard Worker        defined in self.namespace that match.
116*cda5da8dSAndroid Build Coastguard Worker
117*cda5da8dSAndroid Build Coastguard Worker        """
118*cda5da8dSAndroid Build Coastguard Worker        matches = []
119*cda5da8dSAndroid Build Coastguard Worker        seen = {"__builtins__"}
120*cda5da8dSAndroid Build Coastguard Worker        n = len(text)
121*cda5da8dSAndroid Build Coastguard Worker        for word in keyword.kwlist + keyword.softkwlist:
122*cda5da8dSAndroid Build Coastguard Worker            if word[:n] == text:
123*cda5da8dSAndroid Build Coastguard Worker                seen.add(word)
124*cda5da8dSAndroid Build Coastguard Worker                if word in {'finally', 'try'}:
125*cda5da8dSAndroid Build Coastguard Worker                    word = word + ':'
126*cda5da8dSAndroid Build Coastguard Worker                elif word not in {'False', 'None', 'True',
127*cda5da8dSAndroid Build Coastguard Worker                                  'break', 'continue', 'pass',
128*cda5da8dSAndroid Build Coastguard Worker                                  'else', '_'}:
129*cda5da8dSAndroid Build Coastguard Worker                    word = word + ' '
130*cda5da8dSAndroid Build Coastguard Worker                matches.append(word)
131*cda5da8dSAndroid Build Coastguard Worker        for nspace in [self.namespace, builtins.__dict__]:
132*cda5da8dSAndroid Build Coastguard Worker            for word, val in nspace.items():
133*cda5da8dSAndroid Build Coastguard Worker                if word[:n] == text and word not in seen:
134*cda5da8dSAndroid Build Coastguard Worker                    seen.add(word)
135*cda5da8dSAndroid Build Coastguard Worker                    matches.append(self._callable_postfix(val, word))
136*cda5da8dSAndroid Build Coastguard Worker        return matches
137*cda5da8dSAndroid Build Coastguard Worker
138*cda5da8dSAndroid Build Coastguard Worker    def attr_matches(self, text):
139*cda5da8dSAndroid Build Coastguard Worker        """Compute matches when text contains a dot.
140*cda5da8dSAndroid Build Coastguard Worker
141*cda5da8dSAndroid Build Coastguard Worker        Assuming the text is of the form NAME.NAME....[NAME], and is
142*cda5da8dSAndroid Build Coastguard Worker        evaluable in self.namespace, it will be evaluated and its attributes
143*cda5da8dSAndroid Build Coastguard Worker        (as revealed by dir()) are used as possible completions.  (For class
144*cda5da8dSAndroid Build Coastguard Worker        instances, class members are also considered.)
145*cda5da8dSAndroid Build Coastguard Worker
146*cda5da8dSAndroid Build Coastguard Worker        WARNING: this can still invoke arbitrary C code, if an object
147*cda5da8dSAndroid Build Coastguard Worker        with a __getattr__ hook is evaluated.
148*cda5da8dSAndroid Build Coastguard Worker
149*cda5da8dSAndroid Build Coastguard Worker        """
150*cda5da8dSAndroid Build Coastguard Worker        m = re.match(r"(\w+(\.\w+)*)\.(\w*)", text)
151*cda5da8dSAndroid Build Coastguard Worker        if not m:
152*cda5da8dSAndroid Build Coastguard Worker            return []
153*cda5da8dSAndroid Build Coastguard Worker        expr, attr = m.group(1, 3)
154*cda5da8dSAndroid Build Coastguard Worker        try:
155*cda5da8dSAndroid Build Coastguard Worker            thisobject = eval(expr, self.namespace)
156*cda5da8dSAndroid Build Coastguard Worker        except Exception:
157*cda5da8dSAndroid Build Coastguard Worker            return []
158*cda5da8dSAndroid Build Coastguard Worker
159*cda5da8dSAndroid Build Coastguard Worker        # get the content of the object, except __builtins__
160*cda5da8dSAndroid Build Coastguard Worker        words = set(dir(thisobject))
161*cda5da8dSAndroid Build Coastguard Worker        words.discard("__builtins__")
162*cda5da8dSAndroid Build Coastguard Worker
163*cda5da8dSAndroid Build Coastguard Worker        if hasattr(thisobject, '__class__'):
164*cda5da8dSAndroid Build Coastguard Worker            words.add('__class__')
165*cda5da8dSAndroid Build Coastguard Worker            words.update(get_class_members(thisobject.__class__))
166*cda5da8dSAndroid Build Coastguard Worker        matches = []
167*cda5da8dSAndroid Build Coastguard Worker        n = len(attr)
168*cda5da8dSAndroid Build Coastguard Worker        if attr == '':
169*cda5da8dSAndroid Build Coastguard Worker            noprefix = '_'
170*cda5da8dSAndroid Build Coastguard Worker        elif attr == '_':
171*cda5da8dSAndroid Build Coastguard Worker            noprefix = '__'
172*cda5da8dSAndroid Build Coastguard Worker        else:
173*cda5da8dSAndroid Build Coastguard Worker            noprefix = None
174*cda5da8dSAndroid Build Coastguard Worker        while True:
175*cda5da8dSAndroid Build Coastguard Worker            for word in words:
176*cda5da8dSAndroid Build Coastguard Worker                if (word[:n] == attr and
177*cda5da8dSAndroid Build Coastguard Worker                    not (noprefix and word[:n+1] == noprefix)):
178*cda5da8dSAndroid Build Coastguard Worker                    match = "%s.%s" % (expr, word)
179*cda5da8dSAndroid Build Coastguard Worker                    if isinstance(getattr(type(thisobject), word, None),
180*cda5da8dSAndroid Build Coastguard Worker                                  property):
181*cda5da8dSAndroid Build Coastguard Worker                        # bpo-44752: thisobject.word is a method decorated by
182*cda5da8dSAndroid Build Coastguard Worker                        # `@property`. What follows applies a postfix if
183*cda5da8dSAndroid Build Coastguard Worker                        # thisobject.word is callable, but know we know that
184*cda5da8dSAndroid Build Coastguard Worker                        # this is not callable (because it is a property).
185*cda5da8dSAndroid Build Coastguard Worker                        # Also, getattr(thisobject, word) will evaluate the
186*cda5da8dSAndroid Build Coastguard Worker                        # property method, which is not desirable.
187*cda5da8dSAndroid Build Coastguard Worker                        matches.append(match)
188*cda5da8dSAndroid Build Coastguard Worker                        continue
189*cda5da8dSAndroid Build Coastguard Worker                    if (value := getattr(thisobject, word, None)) is not None:
190*cda5da8dSAndroid Build Coastguard Worker                        matches.append(self._callable_postfix(value, match))
191*cda5da8dSAndroid Build Coastguard Worker                    else:
192*cda5da8dSAndroid Build Coastguard Worker                        matches.append(match)
193*cda5da8dSAndroid Build Coastguard Worker            if matches or not noprefix:
194*cda5da8dSAndroid Build Coastguard Worker                break
195*cda5da8dSAndroid Build Coastguard Worker            if noprefix == '_':
196*cda5da8dSAndroid Build Coastguard Worker                noprefix = '__'
197*cda5da8dSAndroid Build Coastguard Worker            else:
198*cda5da8dSAndroid Build Coastguard Worker                noprefix = None
199*cda5da8dSAndroid Build Coastguard Worker        matches.sort()
200*cda5da8dSAndroid Build Coastguard Worker        return matches
201*cda5da8dSAndroid Build Coastguard Worker
202*cda5da8dSAndroid Build Coastguard Workerdef get_class_members(klass):
203*cda5da8dSAndroid Build Coastguard Worker    ret = dir(klass)
204*cda5da8dSAndroid Build Coastguard Worker    if hasattr(klass,'__bases__'):
205*cda5da8dSAndroid Build Coastguard Worker        for base in klass.__bases__:
206*cda5da8dSAndroid Build Coastguard Worker            ret = ret + get_class_members(base)
207*cda5da8dSAndroid Build Coastguard Worker    return ret
208*cda5da8dSAndroid Build Coastguard Worker
209*cda5da8dSAndroid Build Coastguard Workertry:
210*cda5da8dSAndroid Build Coastguard Worker    import readline
211*cda5da8dSAndroid Build Coastguard Workerexcept ImportError:
212*cda5da8dSAndroid Build Coastguard Worker    _readline_available = False
213*cda5da8dSAndroid Build Coastguard Workerelse:
214*cda5da8dSAndroid Build Coastguard Worker    readline.set_completer(Completer().complete)
215*cda5da8dSAndroid Build Coastguard Worker    # Release references early at shutdown (the readline module's
216*cda5da8dSAndroid Build Coastguard Worker    # contents are quasi-immortal, and the completer function holds a
217*cda5da8dSAndroid Build Coastguard Worker    # reference to globals).
218*cda5da8dSAndroid Build Coastguard Worker    atexit.register(lambda: readline.set_completer(None))
219*cda5da8dSAndroid Build Coastguard Worker    _readline_available = True
220