gdbus-codegen: Add an extension system which allows hooking codegen

The extension points are chosen by what libdex needs to generate future
based method calls.
This commit is contained in:
Sebastian Wick
2025-09-16 21:42:28 +02:00
committed by Philip Withnall
parent 07ba38449e
commit 4405fe5fc8
3 changed files with 106 additions and 0 deletions

View File

@@ -47,6 +47,7 @@ SYNOPSIS
| [--annotate *ELEMENT* *KEY* *VALUE*]… | [--annotate *ELEMENT* *KEY* *VALUE*]…
| [--glib-min-required *VERSION*] | [--glib-min-required *VERSION*]
| [--glib-max-allowed *VERSION*] | [--glib-max-allowed *VERSION*]
| [--extension-path *EXTENSION_PATH*]
| *FILE* | *FILE*
DESCRIPTION DESCRIPTION
@@ -392,6 +393,20 @@ The following options are supported:
greater than or equal to that passed to ``--glib-min-required``. greater than or equal to that passed to ``--glib-min-required``.
It defaults to the version of GLib which provides this ``gdbus-codegen``. It defaults to the version of GLib which provides this ``gdbus-codegen``.
``--extension-path`` *EXTENSION_PATH*
Used to load an extension to the codegen. The *EXTENSION_PATH* is a path to
a Python file that will be loaded as a module. The extension needs to define
at least the function ``def init(args, options)`` where ``args`` is a
``argparse.Namespace`` and ``options`` is a dict containing the key
``version``.
All other API the extension can use are internal and thus unstable, but effort
is made to increase the ``version`` field when those internals change. If you
want to use this mechanism, please get in touch by
`filing an issue <https://gitlab.gnome.org/GNOME/glib/-/issues>`_. with your
use case.
SUPPORTED D-BUS ANNOTATIONS SUPPORTED D-BUS ANNOTATIONS
--------------------------- ---------------------------

View File

@@ -41,6 +41,54 @@ LICENSE_STR = """/*
# flake8: noqa: E501 # flake8: noqa: E501
def extensionpoint(func):
def wrapper(self, *args, **kwargs):
# ensures same signature
func(*args, **kwargs)
if not self._ext:
return
method = getattr(self._ext, func.__name__, None)
if not method:
return
method(*args, **kwargs)
return wrapper
class ExtensionGenerator:
def __init__(self, ext, generator):
if self.extending != generator.__class__.__name__:
raise Exception("Trying to extend wrong class")
self._ext = None
klass = getattr(ext, self.extending, None)
if klass:
self._ext = klass(generator)
class ExtensionHeaderCodeGenerator(ExtensionGenerator):
extending = "HeaderCodeGenerator"
@extensionpoint
def generate_includes():
pass
@extensionpoint
def declare_types():
pass
class ExtensionCodeGenerator(ExtensionGenerator):
extending = "CodeGenerator"
@extensionpoint
def generate_body_preamble():
pass
@extensionpoint
def generate():
pass
def generate_namespace(namespace): def generate_namespace(namespace):
ns = namespace ns = namespace
if len(namespace) > 0: if len(namespace) > 0:
@@ -84,6 +132,7 @@ class HeaderCodeGenerator:
symbol_decorator, symbol_decorator,
symbol_decorator_header, symbol_decorator_header,
outfile, outfile,
ext,
): ):
self.ifaces = ifaces self.ifaces = ifaces
self.namespace, self.ns_upper, self.ns_lower = generate_namespace(namespace) self.namespace, self.ns_upper, self.ns_lower = generate_namespace(namespace)
@@ -96,6 +145,7 @@ class HeaderCodeGenerator:
self.symbol_decorator = symbol_decorator self.symbol_decorator = symbol_decorator
self.symbol_decorator_header = symbol_decorator_header self.symbol_decorator_header = symbol_decorator_header
self.outfile = outfile self.outfile = outfile
self.ext = ExtensionHeaderCodeGenerator(ext, self)
# ---------------------------------------------------------------------------------------------------- # ----------------------------------------------------------------------------------------------------
@@ -116,6 +166,7 @@ class HeaderCodeGenerator:
self.outfile.write("\n") self.outfile.write("\n")
self.outfile.write("#include <gio/gio.h>\n") self.outfile.write("#include <gio/gio.h>\n")
self.ext.generate_includes()
self.outfile.write("\n") self.outfile.write("\n")
self.outfile.write("G_BEGIN_DECLS\n") self.outfile.write("G_BEGIN_DECLS\n")
self.outfile.write("\n") self.outfile.write("\n")
@@ -360,6 +411,7 @@ class HeaderCodeGenerator:
" GError **error);\n" " GError **error);\n"
) )
self.outfile.write("\n") self.outfile.write("\n")
self.outfile.write("\n") self.outfile.write("\n")
# Then the property accessor declarations # Then the property accessor declarations
@@ -1014,6 +1066,7 @@ class HeaderCodeGenerator:
def generate(self): def generate(self):
self.generate_header_preamble() self.generate_header_preamble()
self.declare_types() self.declare_types()
self.ext.declare_types()
self.generate_header_postamble() self.generate_header_postamble()
@@ -1458,6 +1511,7 @@ class CodeGenerator:
glib_min_required, glib_min_required,
symbol_decoration_define, symbol_decoration_define,
outfile, outfile,
ext,
): ):
self.ifaces = ifaces self.ifaces = ifaces
self.namespace, self.ns_upper, self.ns_lower = generate_namespace(namespace) self.namespace, self.ns_upper, self.ns_lower = generate_namespace(namespace)
@@ -1469,6 +1523,7 @@ class CodeGenerator:
self.symbol_decoration_define = symbol_decoration_define self.symbol_decoration_define = symbol_decoration_define
self.outfile = outfile self.outfile = outfile
self.marshallers = set() self.marshallers = set()
self.ext = ExtensionCodeGenerator(ext, self)
# ---------------------------------------------------------------------------------------------------- # ----------------------------------------------------------------------------------------------------
@@ -5478,6 +5533,7 @@ class CodeGenerator:
def generate(self): def generate(self):
self.generate_body_preamble() self.generate_body_preamble()
self.ext.generate_body_preamble()
for i in self.ifaces: for i in self.ifaces:
self.generate_generic_marshallers(i) self.generate_generic_marshallers(i)
for i in self.ifaces: for i in self.ifaces:
@@ -5496,3 +5552,4 @@ class CodeGenerator:
if self.generate_objmanager: if self.generate_objmanager:
self.generate_object() self.generate_object()
self.generate_object_manager_client() self.generate_object_manager_client()
self.ext.generate()

View File

@@ -26,6 +26,8 @@
import argparse import argparse
import os import os
import sys import sys
import importlib.util
import traceback
from contextlib import contextmanager from contextlib import contextmanager
from . import config from . import config
@@ -38,6 +40,16 @@ from . import codegen_rst
from .utils import print_error, print_warning from .utils import print_error, print_warning
def import_from_path(module_name, file_path):
spec = importlib.util.spec_from_file_location(module_name, file_path)
if not spec:
raise Exception("Not a Python file")
module = importlib.util.module_from_spec(spec)
sys.modules[module_name] = module
spec.loader.exec_module(module)
return module
def find_arg(arg_list, arg_name): def find_arg(arg_list, arg_name):
for a in arg_list: for a in arg_list:
if a.name == arg_name: if a.name == arg_name:
@@ -274,6 +286,12 @@ def codegen_main():
help="Additional define required for decorator specified by " help="Additional define required for decorator specified by "
"--symbol-decorator", "--symbol-decorator",
) )
arg_parser.add_argument(
"--extension-path",
metavar="EXTENSION_PATH",
default="",
help="Path to a gdbus-codegen Python extension file (unstable API)",
)
group = arg_parser.add_mutually_exclusive_group() group = arg_parser.add_mutually_exclusive_group()
group.add_argument( group.add_argument(
@@ -305,6 +323,20 @@ def codegen_main():
args = arg_parser.parse_args() args = arg_parser.parse_args()
codegen_ext = {}
if args.extension_path:
try:
codegen_ext = import_from_path("GDBusCodegenExt", args.extension_path)
codegen_ext.init(
args,
{
"version": 1,
},
)
except Exception:
print_warning(traceback.format_exc())
print_error("Loading extension {} failed".format(args.extension_path))
if len(args.xml_files) > 0: if len(args.xml_files) > 0:
print_warning( print_warning(
'The "--xml-files" option is deprecated; use positional arguments instead' 'The "--xml-files" option is deprecated; use positional arguments instead'
@@ -479,6 +511,7 @@ def codegen_main():
args.symbol_decorator, args.symbol_decorator,
args.symbol_decorator_header, args.symbol_decorator_header,
outfile, outfile,
codegen_ext,
) )
gen.generate() gen.generate()
@@ -494,6 +527,7 @@ def codegen_main():
glib_min_required, glib_min_required,
args.symbol_decorator_define, args.symbol_decorator_define,
outfile, outfile,
codegen_ext,
) )
gen.generate() gen.generate()