click-contrib/click-option-group

Interoperability with asyncclick

overclockworked64 opened this issue · 2 comments

I apologize if I'm knocking on the wrong door.

This is a minimal example that reproduces my issue:

import asyncclick as click
from click_option_group import optgroup
from click_option_group import RequiredAnyOptionGroup

class PortRange(click.ParamType):
   ...

@click.command()
@click.argument('target')
@click.option('--interface', '-i', type=str, required=True)
@optgroup.group('Ports and/or port ranges', cls=RequiredAnyOptionGroup)
@optgroup.option('--port', '-p', type=click.IntRange(min=0, max=65535), multiple=True)
@optgroup.option('--range', '-r', 'range_', type=PortRange(), multiple=True)
async def main(*args, **kwargs) -> None:
    ...

if __name__ == '__main__':
    main(_anyio_backend='trio')

This is the full traceback:

❯ poetry run python -m wrath 192.168.1.1 -i enp5s0
Traceback (most recent call last):
  File "/usr/lib/python3.8/runpy.py", line 194, in _run_module_as_main
    return _run_code(code, main_globals, None,
  File "/usr/lib/python3.8/runpy.py", line 87, in _run_code
    exec(code, run_globals)
  File "/home/alex/Repositories/overclockworked64/wrath/wrath/__main__.py", line 11, in <module>
    async def main(*args, **kwargs) -> None:
  File "/home/alex/.cache/pypoetry/virtualenvs/wrath-NjAFd-T0-py3.8/lib/python3.8/site-packages/click_option_group/_decorators.py", line 137, in decorator
    func = option_group.option(*item.param_decls, **item.attrs)(func)
  File "/home/alex/.cache/pypoetry/virtualenvs/wrath-NjAFd-T0-py3.8/lib/python3.8/site-packages/click_option_group/_core.py", line 174, in decorator
    func = click.option(*param_decls, group=self, **option_attrs)(func)
  File "/home/alex/.cache/pypoetry/virtualenvs/wrath-NjAFd-T0-py3.8/lib/python3.8/site-packages/click/decorators.py", line 247, in decorator
    _param_memo(f, OptionClass(param_decls, **option_attrs))
  File "/home/alex/.cache/pypoetry/virtualenvs/wrath-NjAFd-T0-py3.8/lib/python3.8/site-packages/click_option_group/_core.py", line 31, in __init__
    super().__init__(param_decls, **attrs)
  File "/home/alex/.cache/pypoetry/virtualenvs/wrath-NjAFd-T0-py3.8/lib/python3.8/site-packages/click/core.py", line 2465, in __init__
    super().__init__(param_decls, type=type, multiple=multiple, **attrs)
  File "/home/alex/.cache/pypoetry/virtualenvs/wrath-NjAFd-T0-py3.8/lib/python3.8/site-packages/click/core.py", line 2032, in __init__
    self.type = types.convert_type(type, default)
  File "/home/alex/.cache/pypoetry/virtualenvs/wrath-NjAFd-T0-py3.8/lib/python3.8/site-packages/click/types.py", line 1019, in convert_type
    return FuncParamType(ty)
  File "/home/alex/.cache/pypoetry/virtualenvs/wrath-NjAFd-T0-py3.8/lib/python3.8/site-packages/click/types.py", line 158, in __init__
    self.name = func.__name__
AttributeError: 'IntRange' object has no attribute '__name__'

This happens only if I'm using asyncclick; if I use regular click (and, of course, make the function sync) the issue vanishes.

@overclockworked64,

Thanks for the report.

It seems, asyncclick re-implements click data types including ParamType, IntRange and Option. I don't know why they do it, but it does not work with options from click_option_group because click_option_group uses click.Option as base class for GroupedOption. We cannot use asyncclick.Option in this place. asyncclick.ParamType, asyncclick.IntRange are incompatible with the current click.Option class.

I think re-implementing types from click is very strange and hard approach. A lot of hard work and endless incompatibility. And for what? Just to write async CLI functions, is it? What for? I would just use explicit creating/getting event loop or running async func (or anyio wrapper) inside CLI function. Something like this:

import asyncio
import click
...

async def do_something(foo, bar):
    pass

@click.command()
@click.option('--foo')
@click.option('--bar')
def cli(foo, bar):
    asyncio.run(do_something(foo, bar))

Okay, I see that the issue is beyond the realm of click-option-group, as I initially suspected. Thanks for the response.