8000 IPCompleter doesn't work inside tuples/arrays · Issue #14585 · ipython/ipython · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

IPCompleter doesn't work inside tuples/arrays #14585

Closed
divyansshhh opened this issue Nov 21, 2024 · 8 comments
Closed

IPCompleter doesn't work inside tuples/arrays #14585

divyansshhh opened this issue Nov 21, 2024 · 8 comments
Assignees

Comments

@divyansshhh
Copy link

Consider the following snippet -

y = "abc"
x = (1, y.<tab>

This doesn't return any autocomplete suggestions. This is fixed by enabling jedi but can we look into fixing this for the IPCompleter?

This is reproducible on both ipython v8.22.0 and v8.28.0

@krassowski
Copy link
Member

The case of unclosed parenthesis is already handled, but this case is not. Briefly the relevant code is:

def _attr_matches(self, text, include_prefix=True) -> Tuple[Sequence[str], str]:
m2 = re.match(r"(.+)\.(\w*)$", self.line_buffer)
if not m2:
return [], ""
expr, attr = m2.group(1, 2)
obj = self._evaluate_expr(expr)

and then in evaluation we use naive approach of trimming on the left side if there is a syntax error - unless there is not.

except Exception as e:
if self.debug:
print("Evaluation exception", e)
# trim the expression to remove any invalid prefix
# e.g. user starts `(d[`, so we get `expr = '(d'`,
# where parenthesis is not closed.
# TODO: make this faster by reusing parts of the computation?
expr = expr[1:]

I think that the problem might be that 1, y is a valid Python expression which evaluates to a tuple (assuming that y is defined).

Partial parsing of Python is not easy in the general case. We could possibly consider some heuristics for closing parenthesis for tuples and lists and after guarded evaluation extract the relevant part.

To spell it out we cannot just split on space as in general case it could be x = (1, a[" "].y.

@krassowski
Copy link
Member

Indeed, <tab> returns results corresponding to methods available on a tuple:

Image

Image

@krassowski
Copy link
Member
krassowski commented Nov 21, 2024

Some more related cases:

  • x = (1, a[" "].y.
  • x = (")", y.
  • x = [1, y.
  • x = {1, y.
x = (
   1, y.

@Carreau Carreau self-assigned this Nov 25, 2024
@Carreau
Copy link
Member
Carreau commented Nov 25, 2024

I'm wondering, finding completions using jedi is a problem, (if you set use_jedi = False, but maybe we an use parso (or even jedi itself but not as a completer), to get just the expression we are trying to complete.

@mlucool
Copy link
mlucool commented Nov 27, 2024

FWIW we turned off jedi because it had historically been too slow, so using Jedi as a solution is unlikely to be ideal (unless its much faster than it used to be)

@mlucool
Copy link
mlucool commented Nov 27, 2024

The following is also a fix

ip = get_ipython()
ip.Completer.python_matcher = ip.Completer.python_matches

@Carreau
Copy link
Member
Carreau commented Nov 28, 2024

FWIW we turned off jedi because it had historically been too slow, so using Jedi as a solution is unlikely to be ideal (unless its much faster than it used to be)

I think it's computing the completions that is slow with jedi – because of all the inference and need to access libraries code; Jedi (and its dependency, parso), also offer the ability to simply parse incomplete Python code – which should be much faster – I'm suggesting using only the parsing to extract the current expression the cursor is in.

@krassowski pointed out that a disadvantage is jedi/parso need a new release on every new Python version because of Python syntax changes so that may be a better reason to not use it.

@krassowski
Copy link
Member

I think restoring the previous behaviour by adding back the attempt to evaluate:

# Another option, seems to work great. Catches things like ''.<tab>
m = re.match(r"(\S+(\.\w+)*)\.(\w*)$", text)

prior to:

m2 = re.match(r"(.+)\.(\w*)$", self.line_buffer)

would be a sensible solution improving the current situation. In the edge cases like x = (1, a[" "].y.<tab> it does not matter as it does not work right now either. However, we should be careful not to brake a[" "].y.<tab>, so maybe it needs two checks? Definitely it needs more tests.

Carreau added a commit that referenced this issue Dec 8, 2024
In progress work toward #14585


guarded eval strip leading characters until it find soemthing, this is
problematic as `(1, x`, becomes valid after 1 char strip: `1, x` is a
tuple;

So now we trim until it is valid an not a tuple. 

This is still imperfect as things like `(1, a[" "].y` will be trimmed to
`y`, while it should stop with `a[" "].y` ?

I think maybe we should back-propagate; build back up from `y`, to `a["
"].y`, greedily until we get the last valid expression – skipping any
unbalanced parentheses/quotes if we encounter imblanced.
@Carreau Carreau closed this as completed Dec 9, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants
0