Dict key completion gives nothing when the subscript is on the right of an assignment (regression in 9.6)
Since 9.6, d["<Tab> no longer completes keys when it sits on the right-hand side of an assignment:
d = {"complete_me": 1, "bar": 2}
x = d["comp<Tab>
d["k"] = d["comp<Tab>Both offer nothing. d["comp<Tab> on its own still works. I first noticed it with pandas (df["k"] = df["Year<Tab> used to offer the matching columns), but a plain dict shows the same thing.
I hit this on 9.17.1 and bisected by release. Script:
from IPython.terminal.interactiveshell import TerminalInteractiveShell
from IPython.core.completer import provisionalcompleter
ip = TerminalInteractiveShell.instance()
ip.user_ns["d"] = {"complete_me": 1, "bar": 2}
lines = [
'd["comp',
'print(d["comp',
'd["bar"]; d["comp',
'x = d["comp',
'd["k"] = d["comp',
'for k in d["comp',
'lambda: d["comp',
'd["bar"] * d["comp',
]
for line in lines:
with provisionalcompleter():
comps = [c.text for c in ip.Completer.completions(line, len(line))]
print(f"{line!r:20} -> {comps}")| line | 9.4.0 | 9.5.0 | 9.6.0 |
|---|---|---|---|
d["comp |
['complete_me'] |
['complete_me'] |
['complete_me'] |
print(d["comp |
['complete_me'] |
['complete_me'] |
['complete_me'] |
d["bar"]; d["comp |
['complete_me'] |
['complete_me'] |
['complete_me'] |
x = d["comp |
['complete_me'] |
['complete_me'] |
[] |
d["k"] = d["comp |
['complete_me'] |
['complete_me'] |
[] |
for k in d["comp |
['complete_me'] |
[] |
[] |
lambda: d["comp |
['complete_me'] |
[] |
[] |
d["bar"] * d["comp |
[] |
[] |
[] |
9.17.1 is the same as 9.6.0. Default settings (evaluation = "limited").
As far as I can tell this is what happens. DICT_MATCHER_REGEX captures everything from the start of the line up to the last [ as the "dict expression" (the group is just .+), so for x = d["comp it hands x = d to _evaluate_expr. That evaluates the text with guarded_eval and, if that fails, _trim_expr chops characters off the front until something evaluates. Up to 9.5, x = d was a SyntaxError in eval mode, so it got trimmed down to d and the keys came back. #14993 (9.6) made guarded_eval parse in exec mode and handle assignments, so x = d and d["k"] = d now evaluate without error to None, nothing gets trimmed, and there are no keys to offer. (d["bar"]; d still works because the value of the last statement happens to be d.)
The for and lambda rows share the root cause but broke one release earlier. #14943 (9.5) changed the retry in _evaluate_expr from except Exception to except (SyntaxError, TypeError), so a NameError (for k in d trims to k in d), a ValueError for an AST node the evaluator doesn't handle (lambda: d on 9.5), or a GuardRejection (df["a"] * df["b<Tab> with pandas, since Series.__mul__ isn't allowed in limited mode) now ends the loop instead of trimming further. On 9.6 and later lambda: d evaluates without error instead — to None, and from 9.7 to a placeholder function — so it then fails the same way as the assignment rows.
The last row has never worked, but it's the same problem in its plainest form. The regex captures d["bar"] * d, guarded_eval calls int.__mul__(2, d) directly, that returns NotImplemented without raising, and NotImplemented has no keys. The completer is trying to multiply a dict by two in order to find out what its keys are. The only thing that needs evaluating on any of these lines is d.
The lines that still work are the ones that are still a syntax error at the [, like print(d[.
Either trimming on a non-expression result (and on more exception types), or making the regex stop at statement/operator boundaries so it only captures the object being subscripted, would seem to cover all of this.
System: Python 3.12 and 3.14, Linux.
Source: ipython/ipython