Looking for feedback on adding import autocomplete to PyREPL

Keep in mind that this currently lives in a private module inside pyrepl (which is itself private) so the implementation is subject to breaking changes without notice.

However, if you really want to use it, this should do what you want:

>>> from _pyrepl._module_completer import ModuleCompleter
>>> c = ModuleCompleter()
>>> c.get_completions('import pa')
['pathlib']
2 Likes

@tomasr8

>>> from sys import e <TAB> <TAB>
elif        else        enumerate(  eval(       except      exec(       exit(

Can we remove keywords and builtins from the autocomplete set?

A

4 Likes

Awesome, thank you! If this ever becomes public somewhere then I’d definitely be a user.

1 Like

This should be the case already. It was added/fixed in gh-69605: Disable PyREPL module autocomplete fallback on regular completion by loic-simon Ā· Pull Request #134181 Ā· python/cpython Ā· GitHub :slight_smile:

1 Like
>>> sys.version_info
sys.version_info(major=3, minor=15, micro=0, releaselevel='alpha', serial=0)
>>> from _pyrepl._module_completer import ModuleCompleter
>>> c = ModuleCompleter()
>>> f = c.get_completions
>>> f('import pa')
['pathlib']
>>> f('from pathlib import ')
['types']
>>> f('from pathlib import P')
[]
>>> f('from collections import ')
['abc']
>>> f('from collections import deq')
[]
>>> f('from typing import ')
[]
>>> f('from typing import T')
[]

The current behavior is a bit surprising. Is the autocomplete working as expected?

Where are the requirements for the autocomplete defined?

1 Like

Yes, autocompletion of module attributes (members) is not currently supported.

A

1 Like

To make progress on this topic, I prepared a POC with these two ideas:

Both are a bit hacky, but not too complicated changes:

And to make it easy for everyone to test, I prepared a :sparkles: WebAssembly interactive demo :sparkles: (totally hijacked from the PEP 810 demo, thanks a lot Pablo for the hard work[2])!


I personaly find TAB mode more convenient to use (especially with laptops where you must hit a combination of keys to enter F8), but I totally understand the security concern behind it.


  1. the tests are those of TAB mode, I’ll rewrite them when needed ā†©ļøŽ

  2. It even use their Python build, so lazy imports work here too, but :shushing_face: ā†©ļøŽ

4 Likes

This is a really nice approach tbh. I’ve been playing with the demo and the TAB mode makes sense to me at least.

I agree with you that TAB mode is more practical than F8. Having to remember a special function key for this seems annoying especially on laptops where your hitting Fn+F8 or whatever. The double-tab pattern is way more intuitive and its already a habit for most people anyway.

From what i can tell the original concern was mostly about modules getting imported silently which could be slow or have side effects. With the confirmation step that concern goes away. If someone doesnt want to import they just dont hit tab again.

If safety / unintended usage are a concern, one could also go to a true double-tab operation, i.e. similar to a mouse double-click one would have to hit tab twice in short sequence. Hitting tab multiple times with delay would only show the warning but not trigger the import.

Possible positive side effect: we could leave out the message on immediate double-tab. That way experienced users are not bothered with the warning/notification anbout import.

Perhaps I’m too experienced, but sometimes hit tab three times quickly without much thought. (1 to complete a common suffix and 2 show the ā€œmenuā€ of non-unique completions. _pyrepl only needs two tabs for this but my shell needs all three.)
I’m not convinced we need ā€œsecurityā€ here, but if we do, I wouldn’t go with double-tab.


Yeah, F8 was a silly example of ā€œanother keyā€.
Shift+Tab maybe?

1 Like

I brought up the security concern but I also think we should not overdo it, a second TAB is ok.

ā€œTrue double-tabā€ as in double-clicking sounds awkward and unexpected to me (does this even exist in other UIs?), and let’s not start with F8, triple-tabbing or other oddities. :slight_smile:

3 Likes

All right, thanks all for the feedback! I think we can go with the second TAB then :smiley:

Before I open a PR, I have a second interrogation: should we make effort to handle modules printing stuff during import gracefully? If you try to autocomplete from this import [1] in the demo, you’ll see it goes quite messy, but maybe it is rare enough to not bother with it? (exceptions during import are already handled gracefully)

I suppose we could just temporarily catch stdout/stderr and dump their content properly on new lines before re-painting the prompt (that may be better in a follow-up PR, though?)


  1. or from lazy_import demo :shushing_face: ā†©ļøŽ

For anyone interested, my PR implementing auto-complete for attributes has been open for a month now:

There’s still plenty of time, but I’d really love to see this land in 3.15!

3 Likes

Hey LoĆÆc! Sorry for the late reply, I took a pause from cpython for a while :slight_smile: I’ll try to review your PR soon. Feel free to ping me if I forget!

1 Like

Update: @tomasr8 suggested that we can automatically import standard library modules, since

  • there is no risk of unwanted side-effects[1]
  • the import time will always be < a few ms

If anyone think this is not ok, please say so!
Otherwise you can check the new behavior on the WebAssembly demo (with a little hack, please read the note!)


  1. except some easter eggs… ā†©ļøŽ

2 Likes

Yeah, try ā€œfrom this import <TAB>ā€ for some ā€œunwanted side effectsā€ (or an easter egg, as you call it in the footnote) :slightly_smiling_face:

And I notice that ā€œfrom antigravity import <TAB>ā€ says ā€œNo module named antigravityā€, which may be true in pyodide, but isn’t true in the normal REPL…

I’m not sure this sort of weirdness is OK, TBH, even if it is rare.

Yes, this and antigravity are the two sidlib modules with side effects I’m aware of! But I cannot see why anyone would want to import something from them: these easter eggs are always explained as import this / import antigravity, which never trigger automatic import.

Note that antigravity is not suggested by the REPL (eg. if you type from anti<TAB>), precisely because it is not available :smiley: the message you’re seeing is the same as if you try from spam import <TAB>.

I can understand this position, even if I thing the QOL benefits largely outweight the costs.

One thing I would like to do though is to capture any text printed on module import, display it cleanly below the prompt, and then the prompt agao, avoiding the ugly mess that from this import[1] currently is. Would that alleviate your concerns by some measure?


  1. or from lazy_demo import… ā†©ļøŽ

Agreed - I’d be fine with just special-casing those two, to be honest. (In the standard REPL, ā€œfrom antigravity import ā€¦ā€ would open the user’s web browser, and that definitely shouldn’t happen, so special-casing is probably the only good option in practice, just to be safe).

If we’re just talking about stdlib modules, having a policy that they cannot have any side effects without some way to disable those side-effects in autocomplete mode[1] is probably better.

If we’re talking about 3rd party modules, output is the least of my concerns. But this has been discussed already, and if the consensus is that it’s OK, then just because I’m personally still not comfortable with the risks, is no reason to rehash everything yet again.


  1. Where ā€œspecial case the module in autocompleteā€ is one such option :slightly_smiling_face: ā†©ļøŽ

Fine by me! I wrote a small script to try and import all stdlib modules / submodules and ran it (on Windows, masOS and WSL) to check for any side effects: in addition of antigravity and this, I found idlelib.idle and a few __main__ submodules that seem to lack a if __name__ == "__main__"' guard; I’m adding them all to a blacklist.

1 Like

There will need to be a test if there’s going to be a blocklist to make sure it’s up-to-date.