3

Trying to add an "option" to a class implementation of click. Admittedly, Python is not my area of expertise, but it needs to be done. There area already a bunch of "arguments" implemented using this class approach. Anyone know how to get options to work here?

test.py

import click

class OptionGroup(click.Option):
    """Customizing the default click option"""

    def list_options(self, ctx: click.Context):
        """Sorts options in the specified order"""
        # By default, click alphabetically sorts options
        # This method will override that feature
        return self.opts.keys()

@click.option(cls=OptionGroup)
def cli_opt():
    """Command Line Interface to configure options"""
    pass

@cli_opt.command()
@click.option('-d', '--dest', 'dst-ip', type=str)
def dest_ip(dest_ip):
    """Specifies the destination controller IP address"""
    print(f"Dest IP:   ", dest_ip)
    click.echo(dest_ip)

if __name__ == "__main__":
    cli_opt()

This is the output when I run the script...

$ python test.py --help
Traceback (most recent call last):
  File "C:\Users\jfell\repos\test.py", line 13, in <module>
    @click.option(cls=OptionGroup)
     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\jfell\AppData\Roaming\Python\Python311\site-packages\click\decorators.py", line 308, in decorator
    _param_memo(f, OptionClass(param_decls, **option_attrs))
                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\jfell\AppData\Roaming\Python\Python311\site-packages\click\core.py", line 2495, in __init__
    super().__init__(param_decls, type=type, multiple=multiple, **attrs)
  File "C:\Users\jfell\AppData\Roaming\Python\Python311\site-packages\click\core.py", line 2072, in __init__
    self.name, self.opts, self.secondary_opts = self._parse_decls(
                                                ^^^^^^^^^^^^^^^^^^
  File "C:\Users\jfell\AppData\Roaming\Python\Python311\site-packages\click\core.py", line 2640, in _parse_decls
    raise TypeError("Could not determine name for option")
TypeError: Could not determine name for option

Similar code that only implements an argument works fine...

import click

class CommandGroup(click.Group):
    """Customizing the default click group"""

    def list_commands(self, ctx: click.Context):
        """Sorts commands in the specified order"""
        # By default, click alphabetically sorts commands
        # This method will override that feature
        return self.commands.keys()


@click.group(cls=CommandGroup)
def cli():
    """Command Line Interface to send commands"""
    pass


@cli.command("goto-mode")
@click.argument("mode", type=str)
def goto_mode(mode: str):
    """Directs Application Mode Change"""
    click.echo(mode)


if __name__ == "__main__":
    cli()

Output for argument only...

$ python test.py goto-mode Success!
Success!

Script with option added...

import click

class OptionGroup(click.Option):
    """Customizing the default click option"""

    def list_options(self, ctx: click.Context):
        """Sorts options in the specified order"""
        # By default, click alphabetically sorts options
        # This method will override that feature
        return self.opts.keys()


@click.option(cls=OptionGroup)
def cli_opt():
    """Command Line Interface to configure options"""
    pass


@cli_opt.command()
@click.option('--dest', '-d', 'dst-ip', type=str)
def dest_ip(dest_ip):
    """Specifies the destination controller IP address"""
    print(f"Dest IP:   ", dest_ip)
    click.echo(dest_ip)


class CommandGroup(click.Group):
    """Customizing the default click group"""

    def list_commands(self, ctx: click.Context):
        """Sorts commands in the specified order"""
        # By default, click alphabetically sorts commands
        # This method will override that feature
        return self.commands.keys()


@click.group(cls=CommandGroup)
def cli():
    """Command Line Interface to send commands"""
    pass


@cli.command("goto-mode")
@click.argument("mode", type=str)
def goto_mode(mode: str):
    """Directs Application Mode Change"""
    click.echo(mode)


if __name__ == "__main__":
    cli_opt()
    cli()

...yields same error...

$ python test.py --help
Traceback (most recent call last):
  File "C:\Users\jfell\repos\test.py", line 13, in <module>
    @click.option(cls=OptionGroup)
     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\jfell\AppData\Roaming\Python\Python311\site-packages\click\decorators.py", line 308, in decorator
    _param_memo(f, OptionClass(param_decls, **option_attrs))
                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\jfell\AppData\Roaming\Python\Python311\site-packages\click\core.py", line 2495, in __init__
    super().__init__(param_decls, type=type, multiple=multiple, **attrs)
  File "C:\Users\jfell\AppData\Roaming\Python\Python311\site-packages\click\core.py", line 2072, in __init__
    self.name, self.opts, self.secondary_opts = self._parse_decls(
                                                ^^^^^^^^^^^^^^^^^^
  File "C:\Users\jfell\AppData\Roaming\Python\Python311\site-packages\click\core.py", line 2640, in _parse_decls
    raise TypeError("Could not determine name for option")
TypeError: Could not determine name for option
6
  • 1
    It's not clear what you're trying to do. What's list_options supposed to achieve? This method doesn't exist in click. Commented Dec 16, 2022 at 10:22
  • 1
    Basically, list_commands is the rationale for class CommandGroup to exist. It's legacy code and not much I can do about it without justification. list_options is the respective rationale for class CommandOptions. Honestly, I can live without it; I'd just like to see the click options working without having to tie them to a commad. Commented Dec 16, 2022 at 14:33
  • 1
    What's class CommandOptions now? Please read How to create a Minimal, Reproducible Example. Commented Dec 16, 2022 at 15:08
  • Sorry, I meant class OptionGroup. Commented Dec 16, 2022 at 16:38
  • Btw, the posted code is reproducible. Just copy it into a file an run it. Commented Dec 16, 2022 at 16:39

1 Answer 1

1

I'd love to see a better way to do this, but the best that I've been able to come up with, so far, is to add the desired options to all of the click commands.

For example...

@cli.command("goto-mode")
@click.argument("mode", type=str)
@click.option('--dest', '-d', 'dst-ip', type=str, default="192.168.8.8")
def goto_mode(mode: str, dest_ip: str):
    """Directs Application Mode Change"""
    click.echo(mode)
    click.echo(dest_ip)

Although it works, it's somewhat unwieldly in that the option has to be added to each and every command, which also makes for a maintenance headache.

Sign up to request clarification or add additional context in comments.

2 Comments

It's not clear what you're trying to do differently from this in your question.
Supposedly, click allows defining options that are not tied to any particular command. I would like to define options to configure IP settings, so that they would be applied irrespective of whatever command the user requests.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.