Skip to content

Commit

Permalink
draft of parameter conversion
Browse files Browse the repository at this point in the history
this isn't really exactly the right place: we only want to do this in the
IPC version of things, not the lazy.* version, since the lazy.* version
should just have users passing the right things, so this will result in a
bunch of extra useless work. but it's an example of parameter hoisting.

Signed-off-by: Tycho Andersen <[email protected]>
  • Loading branch information
tych0 committed Mar 24, 2024
1 parent bab8983 commit 7f8aef7
Show file tree
Hide file tree
Showing 2 changed files with 65 additions and 7 deletions.
66 changes: 59 additions & 7 deletions libqtile/command/interface.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,11 @@

from __future__ import annotations

import inspect
import traceback
import types
from abc import ABCMeta, abstractmethod
from typing import TYPE_CHECKING
from typing import TYPE_CHECKING, get_args, get_origin

from libqtile import ipc
from libqtile.command.base import CommandError, CommandException, CommandObject, SelectError
Expand Down Expand Up @@ -308,13 +310,63 @@ def call(self, data: tuple[list[SelectorType], str, tuple, dict]) -> tuple[int,
return ERROR, "No such command"

logger.debug("Command: %s(%s, %s)", name, args, kwargs)
try:
# Check if method is bound
if hasattr(cmd, "__self__"):
return SUCCESS, cmd(*args, **kwargs)

def lift_arg(typ, arg):
# for stuff like int | None, allow either
if get_origin(typ) is types.UnionType:
for t in get_args(typ):
if t == types.NoneType:
# special case None? I don't know what this looks like
# coming over IPC
if arg == "":
return None
if arg is None:
return None
continue

try:
return lift_arg(t, arg)
except TypeError:
pass
# uh oh, we couldn't lift it to anything
raise TypeError(f"{arg} is not a {typ}")

if get_origin(typ) is list:
# i am explicitly too lazy to support heterogeneous lists, even
# though it would be easy to do here. that would be an awful
# user api.
for t in get_args(typ):
try:
out = []
for elt in arg:
out.append(lift_arg(t, elt))
except TypeError:
pass
return out
# uh oh, we couldn't lift it to anything
raise TypeError(f"{arg} is not a {typ}")

return typ(arg)

converted_args = []
converted_kwargs = dict()

params = inspect.signature(cmd).parameters

i = 0
for param in params:
# a bit of an assumption...
if param == "self":
converted_args.append(obj)
else:
# If not, pass object as first argument
return SUCCESS, cmd(obj, *args, **kwargs)
converted_args.append(lift_arg(params[param].annotation, args[i]))
i = i + 1

for k, v in kwargs:
converted_kwargs[k] = lift_arg(params[k].annotation, v)

try:
return SUCCESS, cmd(*converted_args, **converted_kwargs)
except CommandError as err:
return ERROR, err.args[0]
except Exception:
Expand Down
6 changes: 6 additions & 0 deletions test/test_command.py
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,12 @@ def test_layout_filter(manager):
assert manager.c.get_groups()["a"]["focus"] == "two"


@call_config
def test_param_hoisting(manager):
manager.c.widget["groupbox"].set_font(font="sans")
manager.c.widget["groupbox"].set_font(font=1)


class FakeCommandObject(CommandObject):
@staticmethod
@expose_command()
Expand Down

0 comments on commit 7f8aef7

Please sign in to comment.