altdesktop / python-dbus-next

🚌 The next great DBus library for Python with asyncio support
https://python-dbus-next.readthedocs.io/en/latest/
MIT License
187 stars 59 forks source link

Add options to explicitly specify DBus signatures in decorators #111

Open ahem opened 2 years ago

ahem commented 2 years ago

This adds options to the @method, @dbus_property and @signal decorators that can be used to explicitly specify the DBus signature.

While I find the use of the type annotations to specify the DBus signatures to be very readable, it has the downside that ordinary Python type annotations cannot be used when creating DBus interfaces. Also, as discussed in #78, any type checker (such as mypy or pyright) that tries to validate types will complain about the DBus annotations, and some editors will flag the annotations as errors because of this.

This PR allows the use of "normal" type annotations by allowing the signatures to be specified in the decorators, like this:


    @method(in_signature="sasu", out_signature="i")
    def a_third_method(self, one: str, two: List[str], three) -> int:
        return 42

    @signal(signature="as")
    def foo_signal(self) -> List[str]:
        return ['result']

    @dbus_property(signature='u')
    def foo_prop(self) -> int:
        return self._foo_prop

    @foo_prop.setter
    def foo_prop(self, val: int):
        self._foo_prop = val

If the signature is specified directly in the decorator the method and return type annotations are not inspected, but I have been careful to ensure that the correct data structures are still created for argument lists and signature trees.

I have added some testcases to the tests in test_decorators to validate the new feature.

ahem commented 2 years ago

The tests are passing, but the flake8 check is erroring on a bunch of things - also in files I haven't touched at all. I can easily fix the formatting across all files but I don't know if you would like that on top of this PR?

acrisci commented 2 years ago

It was caused by a yapf update. Take b74742e41c2f8294f1258edc65c7c3ed5415659f and it should work with no changes.

ahem commented 2 years ago

Okay, now all the tests and linting checks are passing

ahem commented 2 years ago

@acrisci did you have a chance to look at this PR? Is this a feature you are interested in, and if so is there anything I can do to help getting it merged?

JohnFreeborg commented 2 years ago

This sounds very attractive! Does this change also enable the use of named arguments in the client? We like to use those not to enable out of order argument passing or optional arguments, but just clarity to the next code maintainer on what the call is doing. Currently dbus_next doesn't work if you use them.

e.g. @method() def servicemethod(self, argname: 'd', argname2: 'd') -> 'd' return argname * argname2

If I try to call the above like this, it doesn't work: call_servicemethod(argname=1.2, argname2=54.3)

ahem commented 2 years ago

I don't believe that DBus itself supports optional arguments. At least, I think that it requires that all argument lists must be concretely typed. The normal way of supplying optional arguments is something with passing a dict or a "Varying".

I guess that you could argue that a "high level" abstraction interface shouldn't necessarily be limited by limitations from further down in the stack, but I guess in this case the maintainers have decided that it is more sensible for a DBus interface to follow this core rule of DBus communication.

Anyway, this PR doesn't really have anything to do with any of that - it was mostly something I did to enable dbus-next to be used in projects that check type annotation with mypy or pyright.

endrift commented 11 months ago

I would really appreciate some traction on this for the same reason: it lets the type annotations be used as type annotations as opposed to being hijacked for a semantic purpose. mypy doesn't really like that.