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

With argument autocompletion, context is not passed properly #942

Closed
fqxp opened this issue Mar 1, 2018 · 8 comments · Fixed by #1679
Closed

With argument autocompletion, context is not passed properly #942

fqxp opened this issue Mar 1, 2018 · 8 comments · Fixed by #1679
Labels
bug f:completion feature: shell completion
Milestone

Comments

@fqxp
Copy link

fqxp commented Mar 1, 2018

I used the current master revision:

$ pip3 install git+https://github.com/pallets/click.git@55682f6f5348f5220a557f89c3a796321a52aebf

and an executable file testclick:

#!/usr/bin/env python3
import click

def _complete(ctx, args, incomplete):
    return ctx.obj['completions']

@click.group()
@click.pass_context
def entrypoint(ctx):
    pass

@entrypoint.command()
@click.argument('arg', type=click.STRING, autocompletion=_complete)
@click.pass_context
def subcommand(ctx, arg):
    print('arg={}'.format(arg))

entrypoint(obj={'completions': ['abc', 'def', 'ghi', ]})

and enabled bash completion with

eval "$(_TESTCLICK_COMPLETE=source ./testclick)"

Now I am getting an error when trying to use autocompletion:

    $ ./testclick subcommand <TAB>Traceback (most recent call last):
  File "./testclick", line 23, in <module>
    entrypoint(obj={'completions': ['abc', 'def', 'ghi', ]})
  File "/home/user/testclick/venv/lib/python3.6/site-packages/click/core.py", line 731, in __call__
    return self.main(*args, **kwargs)
  File "/home/user/testclick/venv/lib/python3.6/site-packages/click/core.py", line 701, in main
    _bashcomplete(self, prog_name, complete_var)
  File "/home/user/testclick/venv/lib/python3.6/site-packages/click/core.py", line 46, in _bashcomplete
    if bashcomplete(cmd, prog_name, complete_var, complete_instr):
  File "/home/user/testclick/venv/lib/python3.6/site-packages/click/_bashcomplete.py", line 216, in bashcomplete
    return do_complete(cli, prog_name)
  File "/home/user/testclick/venv/lib/python3.6/site-packages/click/_bashcomplete.py", line 205, in do_complete
    for item in get_choices(cli, prog_name, args, incomplete):
  File "/home/user/testclick/venv/lib/python3.6/site-packages/click/_bashcomplete.py", line 186, in get_choices
    ctx, all_args, incomplete, param))
  File "/home/user/testclick/venv/lib/python3.6/site-packages/click/_bashcomplete.py", line 124, in get_user_autocompletions
    incomplete=incomplete)
  File "./testclick", line 7, in _complete
    return ctx.obj['completions']
TypeError: 'NoneType' object is not subscriptable

Expected behavior was to get a list of completions abc def ghi.

I am using Python 3.6.4

@LuckyJosh
Copy link

Im currently not able to test my idea, so please excuse me if for just thinking out loud. 😉

Instead of passing the settings dict to entrypoint you could try passing it to the context_settings kwarg of the .group or .command function.

Refer to the documentation for details:
http://click.pocoo.org/5/commands/#context-defaults

@fqxp
Copy link
Author

fqxp commented Mar 4, 2018

@LuckyJosh: Thanks for the hint, but that won’t suffice as a workaround. In the above example, I provided completions as a static dictionary just for the sake of simplicity. In my case, a network connection object would be passed via the context, and the _complete function would use it to retrieve possible completions. I don’t think context_settings can be used for that.

@LuckyJosh
Copy link

LuckyJosh commented Mar 5, 2018

Alright, in that case I have another idea:
Are you sure that the kwarg obj to entrypoint should get passed to the context?

After having a glimps at the implementation of pass_context I would suspect, that obj={...} just get passed to the wrapped function and is not used any further. The context is passed as the first argument to the decorated function without any interaction with the other arguments it would seem.
This would explain the missing (None) ctx.obj the ErrorMessage is about.

The source code I am refering to is in decorators.py.

@fdavis fdavis added the bug label May 18, 2018
@foresto
Copy link

foresto commented Jul 4, 2018

I don't understand what LuckyJosh is trying to say.

The custom context with obj attached is normally created in BaseCommand.main(). The problem is that in autocomplete mode, _bashcomplete() gets called before the context-creation code executes, and creates its own context (without obj) instead.

It looks to me like we want BaseCommand.main() to create its context in all circumstances, and pass it to _bashcomplete(). Would there be any drawbacks to this?

Possibly related: #930

+1 on this issue. I, too, am trying to get autocomplete working with completions that come from network calls.

@foresto
Copy link

foresto commented Jul 5, 2018

To be clear, this issue limits the utility of #755. Until it is fixed, autocompletion callbacks have no clean way to access shared network resources.

@maxandersen
Copy link

anyone found a workaround on this ?

for now i'm doing things like this to at least init state based on env variables before calling out:

def get_entities(ctx, args, incomplete):
    if not hasattr(ctx, 'server'):
        ctx.server = os.environ.get('HASS_SERVER', const.DEFAULT_SERVER)
    
    if not hasattr(ctx, 'token'):
        ctx.token = os.environ.get('HASS_TOKEN')

    if not hasattr(ctx, "timeout"):
        ctx.timeout = os.environ.get('HASS_TIMEOUT', const.DEFAULT_TIMEOUT)

    try:
        x = req(ctx, "get", "states")
    except HTTPError:
        x = None

    entities = []

    if x is not None:
        for entity in x:
            entities.append((entity["entity_id"], ''))

        return [c for c in entities if incomplete in c[0]]
    else:
        return entities

this lets the autocompletion work but its unforunate I have to relist the defaults and env parameters here. would be so much nicer if there was a way click could pass the right context or i at least could call something to generate the proper config/context.

thx!

@diegoroccia

This comment has been minimized.

@davidag
Copy link

davidag commented Oct 23, 2019

I'm facing this issue too.

For now, my workaround is to factor out the ctx.obj creation code and call it from both cli() and the corresponding autocomplete methods.

# cli() function
@click.group
@click.pass_context
def cli(ctx):
    ctx.obj = create_context_object()

# example autocomplete function
def get_completion(ctx, args, incomplete):
    if ctx.obj is None:
        ctx.obj = create_context_object()

@davidism davidism added the f:completion feature: shell completion label Feb 28, 2020
@davidism davidism added this to the 8.0.0 milestone Oct 3, 2020
@github-actions github-actions bot locked as resolved and limited conversation to collaborators Nov 13, 2020
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
bug f:completion feature: shell completion
Projects
None yet
Development

Successfully merging a pull request may close this issue.

8 participants