Skip to content

Commit 8af9ea3

Browse files
committed
Add --block option to interface generators
Generates blocking interfaces code when passed instead of async.
1 parent d60ba5f commit 8af9ea3

4 files changed

Lines changed: 172 additions & 33 deletions

File tree

docs/code_generator.rst

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@ Interface code generator
33

44
Python-sdbus is able to generate the interfaces code from
55
the D-Bus introspection XML. (either from a file or live object on D-Bus)
6-
Currently only async interfaces code can be generated.
6+
Currently async interfaces code is generated by default.
7+
Blocking interfaces can be generated by passing ``--block`` option.
78

89
Running code generator requires
910
`Jinja2 <https://jinja2docs.readthedocs.io/en/stable/>`_

src/sdbus/__main__.py

Lines changed: 30 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626

2727
from .interface_generator import (
2828
DbusInterfaceIntrospection,
29-
generate_async_py_file,
29+
generate_py_file,
3030
interfaces_from_file,
3131
interfaces_from_str,
3232
)
@@ -37,6 +37,7 @@ def run_gen_from_connection(
3737
object_paths: List[str],
3838
system: bool,
3939
imports_header: bool,
40+
do_async: bool,
4041
) -> None:
4142
connection_name = connection_name
4243
object_paths = object_paths
@@ -55,19 +56,31 @@ def run_gen_from_connection(
5556
interfaces.extend(interfaces_from_str(itrospection))
5657

5758
stdout.write(
58-
generate_async_py_file(
59-
interfaces, imports_header))
59+
generate_py_file(
60+
interfaces,
61+
imports_header,
62+
do_async,
63+
)
64+
)
6065

6166

62-
def run_gen_from_file(filenames: List[str], imports_header: bool) -> None:
67+
def run_gen_from_file(
68+
filenames: List[str],
69+
imports_header: bool,
70+
do_async: bool,
71+
) -> None:
6372
interfaces: List[DbusInterfaceIntrospection] = []
6473

6574
for file in filenames:
6675
interfaces.extend(interfaces_from_file(file))
6776

6877
stdout.write(
69-
generate_async_py_file(
70-
interfaces, imports_header))
78+
generate_py_file(
79+
interfaces,
80+
imports_header,
81+
do_async,
82+
)
83+
)
7184

7285

7386
def generator_main(args: Optional[List[str]] = None) -> None:
@@ -94,6 +107,17 @@ def generator_main(args: Optional[List[str]] = None) -> None:
94107
help="Include 'import' header (default)",
95108
)
96109

110+
subparser.add_argument(
111+
"--async", action="store_true", default=True,
112+
dest="do_async",
113+
help="Generate async interfaces (default)",
114+
)
115+
subparser.add_argument(
116+
"--block", action="store_false",
117+
dest="do_async",
118+
help="Generate blocking interfaces",
119+
)
120+
97121
generate_from_file_parser.add_argument(
98122
'filenames', type=Path, nargs='+',
99123
help="Paths to interface XML introspection files"

src/sdbus/interface_generator.py

Lines changed: 106 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -549,6 +549,32 @@ def __init__(self, element: Element):
549549
}
550550

551551
INTERFACE_TEMPLATES: Dict[str, str] = {
552+
"generic_method_flags": (
553+
r"""
554+
{%- if method.dbus_input_signature %}
555+
input_signature="{{ method.dbus_input_signature }}",
556+
{%- endif %}
557+
558+
{%- if method.dbus_result_signature %}
559+
result_signature="{{ method.dbus_result_signature }}",
560+
{%- endif %}
561+
562+
{%- if method.flags_str %}
563+
flags={{ method.flags_str }},
564+
{%- endif %}
565+
"""
566+
),
567+
"generic_property_flags": (
568+
r"""
569+
{%- if a_property.dbus_signature %}
570+
property_signature="{{ a_property.dbus_signature }}",
571+
{%- endif %}
572+
573+
{%- if a_property.flags_str %}
574+
flags={{ a_property.flags_str }},
575+
{%- endif %}
576+
"""
577+
),
552578
"generic_header": r"""from __future__ import annotations
553579
554580
from typing import Any, Dict, List, Tuple""",
@@ -598,18 +624,9 @@ def __init__(self, element: Element):
598624
"async_method": (
599625
r"""
600626
@dbus_method_async(
601-
602-
{%- if method.dbus_input_signature %}
603-
input_signature="{{ method.dbus_input_signature }}",
604-
{%- endif %}
605-
606-
{%- if method.dbus_result_signature %}
607-
result_signature="{{ method.dbus_result_signature }}",
608-
{%- endif %}
609-
610-
{%- if method.flags_str %}
611-
flags={{ method.flags_str }},
612-
{%- endif %}
627+
{%- filter indent -%}
628+
{%- include 'generic_method_flags' -%}
629+
{%- endfilter %}
613630
)
614631
async def {{ method.python_name }}(
615632
self,
@@ -624,14 +641,9 @@ async def {{ method.python_name }}(
624641
"async_property": (
625642
r"""
626643
@dbus_property_async(
627-
628-
{%- if a_property.dbus_signature %}
629-
property_signature="{{ a_property.dbus_signature }}",
630-
{%- endif %}
631-
632-
{%- if a_property.flags_str %}
633-
flags={{ a_property.flags_str }},
634-
{%- endif %}
644+
{%- filter indent -%}
645+
{%- include 'generic_property_flags' -%}
646+
{%- endfilter %}
635647
)
636648
def {{ a_property.python_name }}(self) -> {{ a_property.typing }}:
637649
raise NotImplementedError"""
@@ -651,6 +663,72 @@ def {{ a_property.python_name }}(self) -> {{ a_property.typing }}:
651663
def {{ signal.python_name }}(self) -> {{ signal.typing }}:
652664
raise NotImplementedError"""
653665
),
666+
"blocking_imports_header": r"""from sdbus import (
667+
DbusDeprecatedFlag,
668+
DbusInterfaceCommon,
669+
DbusNoReplyFlag,
670+
DbusPropertyConstFlag,
671+
DbusPropertyEmitsChangeFlag,
672+
DbusPropertyEmitsInvalidationFlag,
673+
DbusPropertyExplicitFlag,
674+
DbusUnprivilegedFlag,
675+
dbus_method,
676+
dbus_property,
677+
)""",
678+
"blocking_main": (
679+
r"""{% if include_import_header -%}
680+
{% include 'generic_header' %}
681+
682+
{% include 'blocking_imports_header' %}
683+
{%- endif %}
684+
{% for interface in interfaces %}
685+
686+
{% include 'blocking_interface' %}
687+
{%- endfor %}
688+
"""
689+
),
690+
"blocking_interface": (
691+
r"""class {{ interface.python_name }}(
692+
DbusInterfaceCommon,
693+
interface_name="{{ interface.interface_name }}",
694+
):
695+
{%- filter indent -%}
696+
{% for method in interface.methods -%}
697+
{% include 'blocking_method' %}
698+
{% endfor -%}
699+
{% for a_property in interface.properties -%}
700+
{% include 'blocking_property' %}
701+
{% endfor -%}
702+
{%- endfilter -%}
703+
"""
704+
),
705+
"blocking_method": (
706+
r"""
707+
@dbus_method(
708+
{%- filter indent -%}
709+
{%- include 'generic_method_flags' -%}
710+
{%- endfilter %}
711+
)
712+
def {{ method.python_name }}(
713+
self,
714+
715+
{%- for arg_name, arg_type in method.args_names_and_typing %}
716+
{{ arg_name }}: {{ arg_type }},
717+
{%- endfor %}
718+
) -> {{ method.result_typing }}:
719+
raise NotImplementedError
720+
"""
721+
),
722+
"blocking_property": (
723+
r"""
724+
@dbus_property(
725+
{%- filter indent -%}
726+
{%- include 'generic_property_flags' -%}
727+
{%- endfilter %}
728+
)
729+
def {{ a_property.python_name }}(self) -> {{ a_property.typing }}:
730+
raise NotImplementedError"""
731+
),
654732
}
655733

656734

@@ -690,15 +768,19 @@ def interfaces_from_str(xml_str: str) -> List[DbusInterfaceIntrospection]:
690768
return xml_to_interfaces_introspection(etree)
691769

692770

693-
def generate_async_py_file(
694-
interfaces: List[DbusInterfaceIntrospection],
695-
include_import_header: bool = True) -> str:
771+
def generate_py_file(
772+
interfaces: List[DbusInterfaceIntrospection],
773+
include_import_header: bool = True,
774+
do_async: bool = True,
775+
) -> str:
696776

697777
from jinja2 import DictLoader
698778
from jinja2 import Environment as JinjaEnv
699779

780+
template_name = "async_main" if do_async else "blocking_main"
781+
700782
env = JinjaEnv(loader=DictLoader(INTERFACE_TEMPLATES))
701-
return env.get_template("async_main").render(
783+
return env.get_template(template_name).render(
702784
interfaces=interfaces,
703785
include_import_header=include_import_header,
704786
)

test/test_interface_generator.py

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
from sdbus.interface_generator import (
2828
DbusSigToTyping,
2929
camel_case_to_snake_case,
30-
generate_async_py_file,
30+
generate_py_file,
3131
interface_name_to_class,
3232
interfaces_from_str,
3333
)
@@ -196,7 +196,7 @@ def test_parsing(self) -> None:
196196
False,
197197
)
198198

199-
generated = generate_async_py_file(interfaces_intro)
199+
generated = generate_py_file(interfaces_intro)
200200
self.assertIn('flags=DbusPropertyEmitsInvalidationFlag', generated)
201201
self.assertIn('flags=DbusPropertyConstFlag', generated)
202202

@@ -228,6 +228,38 @@ def test_generate_from_connection(self) -> None:
228228
"get_connection_unix_process_id",
229229
generated_interface,
230230
)
231+
self.assertIn(
232+
"async",
233+
generated_interface,
234+
)
235+
236+
def test_generate_from_connection_blocking(self) -> None:
237+
if find_spec('jinja2') is None:
238+
raise SkipTest('Jinja2 not installed')
239+
240+
with patch("sdbus.__main__.stdout") as stdout_mock:
241+
generator_main(
242+
[
243+
"gen-from-connection",
244+
"--block",
245+
"org.freedesktop.DBus",
246+
"/org/freedesktop/DBus",
247+
]
248+
)
249+
250+
write_mock: MagicMock = stdout_mock.write
251+
write_mock.assert_called_once()
252+
253+
generated_interface = write_mock.call_args.args[0]
254+
255+
self.assertNotIn(
256+
"async",
257+
generated_interface,
258+
)
259+
self.assertIn(
260+
"dbus_property",
261+
generated_interface,
262+
)
231263

232264

233265
if __name__ == "__main__":

0 commit comments

Comments
 (0)