python-miio icon indicating copy to clipboard operation
python-miio copied to clipboard

Breaking change with click dependency >= 8.2.0 / miiocli unusable with latest update

Open OevreFlataeker opened this issue 8 months ago • 3 comments

The pyproject.toml just constraints click to be >=8. However starting with click 8.2.0 the miiocli is unusable throwing dozens of errors.

The pyproject.toml should constraint to the maximum version of 8.1.8

(.venv) daubsi@t14:/tmp/.venv$ pip install click==8.2.0
Collecting click==8.2.0
  Using cached click-8.2.0-py3-none-any.whl.metadata (2.5 kB)
Using cached click-8.2.0-py3-none-any.whl (102 kB)
Installing collected packages: click
  Attempting uninstall: click
    Found existing installation: click 8.1.8
    Uninstalling click-8.1.8:
      Successfully uninstalled click-8.1.8
Successfully installed click-8.2.0
(.venv) daubsi@t14:/tmp/.venv$ ./bin/miiocli fan
ERROR:miio.click_common:Exception: sequence item 1: expected str instance, bool found
Traceback (most recent call last):
  File "/tmp/.venv/lib/python3.12/site-packages/miio/click_common.py", line 55, in __call__
    return self.main(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/tmp/.venv/lib/python3.12/site-packages/click/core.py", line 1363, in main
    rv = self.invoke(ctx)
         ^^^^^^^^^^^^^^^^
  File "/tmp/.venv/lib/python3.12/site-packages/click/core.py", line 1828, in invoke
    sub_ctx = cmd.make_context(cmd_name, args, parent=ctx)
              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/tmp/.venv/lib/python3.12/site-packages/click/core.py", line 1186, in make_context
    self.parse_args(ctx, args)
  File "/tmp/.venv/lib/python3.12/site-packages/click/core.py", line 1784, in parse_args
    raise NoArgsIsHelpError(ctx)
          ^^^^^^^^^^^^^^^^^^^^^^
  File "/tmp/.venv/lib/python3.12/site-packages/click/exceptions.py", line 271, in __init__
    super().__init__(ctx.get_help(), ctx=ctx)
                     ^^^^^^^^^^^^^^
  File "/tmp/.venv/lib/python3.12/site-packages/click/core.py", line 730, in get_help
    return self.command.get_help(self)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/tmp/.venv/lib/python3.12/site-packages/click/core.py", line 1064, in get_help
    self.format_help(ctx, formatter)
  File "/tmp/.venv/lib/python3.12/site-packages/click/core.py", line 1102, in format_help
    self.format_usage(ctx, formatter)
  File "/tmp/.venv/lib/python3.12/site-packages/click/core.py", line 1003, in format_usage
    formatter.write_usage(ctx.command_path, " ".join(pieces))
                                            ^^^^^^^^^^^^^^^^
TypeError: sequence item 1: expected str instance, bool found
(.venv) daubsi@t14:/tmp/.venv$ pip install click==8.1.8
Collecting click==8.1.8
  Using cached click-8.1.8-py3-none-any.whl.metadata (2.3 kB)
Using cached click-8.1.8-py3-none-any.whl (98 kB)
Installing collected packages: click
  Attempting uninstall: click
    Found existing installation: click 8.2.0
    Uninstalling click-8.2.0:
      Successfully uninstalled click-8.2.0
Successfully installed click-8.1.8
(.venv) daubsi@t14:/tmp/.venv$ ./bin/miiocli fan
Usage: miiocli fan [OPTIONS] COMMAND [ARGS]...

Options:
  --ip TEXT     [required]
  --token TEXT  [required]
  --model TEXT
  --help        Show this message and exit.

Commands:
  actions             Return device actions.
  call                Call action by name.
  delay_off           Set delay off seconds.
  descriptors         Return a collection containing all descriptors for...
  info                Get (and cache) miIO protocol information from the...
  off                 Power off.
  on                  Power on.
  raw_command         Send a raw command to the device.
  sensors             Return read-only properties.
  set                 Change setting value.
  set_angle           Set the oscillation angle.
  set_buzzer          Set buzzer on/off.
  set_child_lock      Set child lock on/off.
  set_direct_speed    Set speed of the direct mode.
  set_led             Turn led on/off.
  set_led_brightness  Set led brightness.
  set_natural_speed   Set natural level.
  set_oscillate       Set oscillate on/off.
  set_power           Turn device on or off.
  set_rotate          Rotate the fan by -5/+5 degrees left/right.
  settings            Return settable properties.
  status              Retrieve properties.

OevreFlataeker avatar Jun 22 '25 20:06 OevreFlataeker

The dependency pin is left on purpose quite broad, as this is used as a library. I'm wondering if this is a regression in click, or if it's something that can be fixed in this project.

Alas, I'm not sure when I will find some time to contribute to this project, so it might take some time to figure out a solution. If you or anyone reading this wants to help, I can do code reviews and merge PRs, though.

rytilahti avatar Jun 22 '25 22:06 rytilahti

Spend a couple of hours today (without prior knowledge about click or python-miio) trying to understand how this all interrelates with the decorations etc. The actual exception is raised in click's core.py as one can see in the stacktrace above, where one argument (subcommand_metavar) which is expected to always only be a str or None all of a sudden is a bool. I was not yet able to trace when or how this happens.

However trying to understand how all those @click decorations are build I stumbled over click_common.py in python-miio which as far as I understand dynamically builds all the relevant structures for click? I can only suspect that something in here is not 100% clean and causes the glitch from a str to a bool.

I noticed there was also a TODO in the code in click_common.py

            # TODO HACK to make the command visible to cli
            _wrap._device_group_command = func._device_group_command

which might be related...

Apart from that I don´t see any change in click's version changelog (https://click.palletsprojects.com/en/stable/changes/) which might be fitting to what can be seen as a result of the different behavior between 8.1.8 and 8.2.0 of the library.

My attempt of getting some ideas from click's devs was not fruitful so far.

P.S. Manually changing the subcommand_metavar during a debugsession from bool to a string was not successfull. The particular:

File "/tmp/.venv/lib/python3.12/site-packages/click/core.py", line 1003, in format_usage
    formatter.write_usage(ctx.command_path, " ".join(pieces))

did no longer throw an exception but another one followed basically saying that no commands were found which let me assume it might be related to the dynamic building of all the annotations.

OevreFlataeker avatar Jun 23 '25 20:06 OevreFlataeker

确实,click降到8.1.8能够正常使用了,感谢。

zhu258258669 avatar Sep 23 '25 11:09 zhu258258669

Thanks @zhu258258669 - in english, downgrade click to 8.1.8

pip install click==8.1.8

npike avatar Dec 11 '25 04:12 npike