Skip to content

Commit ffb374c

Browse files
author
Anders Hellerup Madsen
committed
support signatures in method decorator
1 parent b74742e commit ffb374c

2 files changed

Lines changed: 61 additions & 23 deletions

File tree

dbus_next/service.py

Lines changed: 42 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -6,32 +6,47 @@
66

77
from functools import wraps
88
import inspect
9-
from typing import no_type_check_decorator, Dict, List, Any
9+
from typing import no_type_check_decorator, Dict, List, Any, Optional
1010
import copy
1111
import asyncio
1212

1313

1414
class _Method:
15-
def __init__(self, fn, name, disabled=False):
16-
in_signature = ''
17-
out_signature = ''
15+
def __init__(self,
16+
fn,
17+
name,
18+
disabled=False,
19+
in_signature: Optional[str] = None,
20+
out_signature: Optional[str] = None):
1821

1922
inspection = inspect.signature(fn)
2023

21-
in_args = []
22-
for i, param in enumerate(inspection.parameters.values()):
23-
if i == 0:
24-
# first is self
25-
continue
26-
annotation = parse_annotation(param.annotation)
27-
if not annotation:
28-
raise ValueError(
29-
'method parameters must specify the dbus type string as an annotation')
30-
in_args.append(intr.Arg(annotation, intr.ArgDirection.IN, param.name))
31-
in_signature += annotation
24+
if in_signature is None:
25+
in_signature = ''
26+
in_args = []
27+
for i, param in enumerate(inspection.parameters.values()):
28+
if i == 0:
29+
# first is self
30+
continue
31+
annotation = parse_annotation(param.annotation)
32+
if not annotation:
33+
raise ValueError(
34+
'method parameters must specify the dbus type string as an annotation')
35+
in_args.append(intr.Arg(annotation, intr.ArgDirection.IN, param.name))
36+
in_signature += annotation
37+
else:
38+
name_iter = iter(inspection.parameters.keys())
39+
next(name_iter) # skip self parameter
40+
in_args = [
41+
intr.Arg(type_, intr.ArgDirection.IN, name)
42+
for name, type_ in zip(name_iter,
43+
SignatureTree._get(in_signature).types)
44+
]
45+
46+
if out_signature is None:
47+
out_signature = parse_annotation(inspection.return_annotation)
3248

3349
out_args = []
34-
out_signature = parse_annotation(inspection.return_annotation)
3550
if out_signature:
3651
for type_ in SignatureTree._get(out_signature).types:
3752
out_args.append(intr.Arg(type_, intr.ArgDirection.OUT))
@@ -41,12 +56,15 @@ def __init__(self, fn, name, disabled=False):
4156
self.disabled = disabled
4257
self.introspection = intr.Method(name, in_args, out_args)
4358
self.in_signature = in_signature
44-
self.out_signature = out_signature
4559
self.in_signature_tree = SignatureTree._get(in_signature)
60+
self.out_signature = out_signature
4661
self.out_signature_tree = SignatureTree._get(out_signature)
4762

4863

49-
def method(name: str = None, disabled: bool = False):
64+
def method(name: str = None,
65+
disabled: bool = False,
66+
in_signature: Optional[str] = None,
67+
out_signature: Optional[str] = None):
5068
"""A decorator to mark a class method of a :class:`ServiceInterface` to be a DBus service method.
5169
5270
The parameters and return value must each be annotated with a signature
@@ -91,7 +109,12 @@ def wrapped(*args, **kwargs):
91109
fn(*args, **kwargs)
92110

93111
fn_name = name if name else fn.__name__
94-
wrapped.__dict__['__DBUS_METHOD'] = _Method(fn, fn_name, disabled=disabled)
112+
_method = _Method(fn,
113+
fn_name,
114+
disabled=disabled,
115+
in_signature=in_signature,
116+
out_signature=out_signature)
117+
wrapped.__dict__['__DBUS_METHOD'] = _method
95118

96119
return wrapped
97120

test/service/test_decorators.py

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
from dbus_next import PropertyAccess, introspection as intr
22
from dbus_next.service import method, signal, dbus_property, ServiceInterface
33

4+
from typing import List
5+
46

57
class ExampleInterface(ServiceInterface):
68
def __init__(self):
@@ -17,6 +19,10 @@ def some_method(self, one: 's', two: 's') -> 's':
1719
def another_method(self, eight: 'o', six: 't'):
1820
pass
1921

22+
@method(in_signature="sasu", out_signature="i")
23+
def a_third_method(self, one: str, two: List[str], three) -> int:
24+
return 42
25+
2026
@signal()
2127
def some_signal(self) -> 'as':
2228
return ['result']
@@ -56,16 +62,25 @@ def test_method_decorator():
5662
methods = ServiceInterface._get_methods(interface)
5763
signals = ServiceInterface._get_signals(interface)
5864

59-
assert len(methods) == 2
65+
assert len(methods) == 3
6066

6167
method = methods[0]
68+
assert method.name == 'a_third_method'
69+
assert method.in_signature == 'sasu'
70+
assert method.out_signature == 'i'
71+
assert not method.disabled
72+
assert type(method.introspection) is intr.Method
73+
assert len(method.introspection.in_args) == 3
74+
assert len(method.introspection.out_args) == 1
75+
76+
method = methods[1]
6277
assert method.name == 'renamed_method'
6378
assert method.in_signature == 'ot'
6479
assert method.out_signature == ''
6580
assert method.disabled
6681
assert type(method.introspection) is intr.Method
6782

68-
method = methods[1]
83+
method = methods[2]
6984
assert method.name == 'some_method'
7085
assert method.in_signature == 'ss'
7186
assert method.out_signature == 's'
@@ -142,7 +157,7 @@ def test_interface_introspection():
142157
signals = xml.findall('signal')
143158
properties = xml.findall('property')
144159

145-
assert len(xml) == 4
146-
assert len(methods) == 1
160+
assert len(xml) == 5
161+
assert len(methods) == 2
147162
assert len(signals) == 1
148163
assert len(properties) == 2

0 commit comments

Comments
 (0)