summaryrefslogtreecommitdiff
path: root/src/py
diff options
context:
space:
mode:
authorLaurent Pinchart <laurent.pinchart@ideasonboard.com>2024-08-08 03:41:32 +0300
committerLaurent Pinchart <laurent.pinchart@ideasonboard.com>2024-08-16 00:28:32 +0300
commit39f01e9185857a872e2ae4617d8ea1bb341539f8 (patch)
treecdd0d0280298e32b3ce2894450cb013a7f053e35 /src/py
parent5c1cb5e5bcc279a38b3947e9050ef4cd9e4ae0fd (diff)
py: gen-py-controls: Convert to jinja2 templates
Jinja2 templates help separate the logic related to the template from the generation of the data. The python code becomes much clearer as a result. As an added bonus, we can use a single template file for both controls and properties. Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com> Reviewed-by: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com> Acked-by: Paul Elder <paul.elder@ideasonboard.com>
Diffstat (limited to 'src/py')
-rwxr-xr-xsrc/py/libcamera/gen-py-controls.py118
-rw-r--r--src/py/libcamera/meson.build8
-rw-r--r--src/py/libcamera/py_controls_generated.cpp.in35
-rw-r--r--src/py/libcamera/py_properties_generated.cpp.in30
4 files changed, 80 insertions, 111 deletions
diff --git a/src/py/libcamera/gen-py-controls.py b/src/py/libcamera/gen-py-controls.py
index a18dc533..cf09c146 100755
--- a/src/py/libcamera/gen-py-controls.py
+++ b/src/py/libcamera/gen-py-controls.py
@@ -4,7 +4,7 @@
# Generate Python bindings controls from YAML
import argparse
-import string
+import jinja2
import sys
import yaml
@@ -23,67 +23,39 @@ def find_common_prefix(strings):
return prefix
-def generate_py(controls, mode):
- out = ''
-
- vendors_class_def = []
- vendor_defs = []
- vendors = []
- for vendor, ctrl_list in controls.items():
- for ctrl in ctrl_list:
- if vendor not in vendors and vendor != 'libcamera':
- vendor_mode_str = f'{vendor.capitalize()}{mode.capitalize()}'
- vendors_class_def.append('class Py{}\n{{\n}};\n'.format(vendor_mode_str))
- vendor_defs.append('\tauto {} = py::class_<Py{}>(controls, \"{}\");'.format(vendor, vendor_mode_str, vendor))
- vendors.append(vendor)
-
- if vendor != 'libcamera':
- ns = 'libcamera::{}::{}::'.format(mode, vendor)
- container = vendor
- else:
- ns = 'libcamera::{}::'.format(mode)
- container = 'controls'
-
- out += f'\t{container}.def_readonly_static("{ctrl.name}", static_cast<const libcamera::ControlId *>(&{ns}{ctrl.name}));\n\n'
-
- if not ctrl.is_enum:
- continue
-
- cpp_enum = ctrl.name + 'Enum'
-
- out += '\tpy::enum_<{}{}>({}, \"{}\")\n'.format(ns, cpp_enum, container, cpp_enum)
-
- if mode == 'controls':
- # Adjustments for controls
- if ctrl.name == 'LensShadingMapMode':
- prefix = 'LensShadingMapMode'
- else:
- prefix = find_common_prefix([e.name for e in ctrl.enum_values])
- else:
- # Adjustments for properties
- prefix = find_common_prefix([e.name for e in ctrl.enum_values])
-
- for entry in ctrl.enum_values:
- cpp_enum = entry.name
- py_enum = entry.name[len(prefix):]
-
- out += '\t\t.value(\"{}\", {}{})\n'.format(py_enum, ns, cpp_enum)
-
- out += '\t;\n\n'
-
- return {'controls': out,
- 'vendors_class_def': '\n'.join(vendors_class_def),
- 'vendors_defs': '\n'.join(vendor_defs)}
+def extend_control(ctrl, mode):
+ if ctrl.vendor != 'libcamera':
+ ctrl.klass = ctrl.vendor
+ ctrl.namespace = f'{ctrl.vendor}::'
+ else:
+ ctrl.klass = mode
+ ctrl.namespace = ''
+
+ if not ctrl.is_enum:
+ return ctrl
+
+ if mode == 'controls':
+ # Adjustments for controls
+ if ctrl.name == 'LensShadingMapMode':
+ prefix = 'LensShadingMapMode'
+ else:
+ prefix = find_common_prefix([e.name for e in ctrl.enum_values])
+ else:
+ # Adjustments for properties
+ prefix = find_common_prefix([e.name for e in ctrl.enum_values])
+ for enum in ctrl.enum_values:
+ enum.py_name = enum.name[len(prefix):]
-def fill_template(template, data):
- template = open(template, 'rb').read()
- template = template.decode('utf-8')
- template = string.Template(template)
- return template.substitute(data)
+ return ctrl
def main(argv):
+ headers = {
+ 'controls': 'control_ids.h',
+ 'properties': 'property_ids.h',
+ }
+
# Parse command line arguments
parser = argparse.ArgumentParser()
parser.add_argument('--mode', '-m', type=str, required=True,
@@ -96,27 +68,41 @@ def main(argv):
help='Input file name.')
args = parser.parse_args(argv[1:])
- if args.mode not in ['controls', 'properties']:
+ if not headers.get(args.mode):
print(f'Invalid mode option "{args.mode}"', file=sys.stderr)
return -1
- controls = {}
+ controls = []
+ vendors = []
+
for input in args.input:
data = yaml.safe_load(open(input, 'rb').read())
+
vendor = data['vendor']
- ctrls = data['controls']
- controls[vendor] = [Control(*ctrl.popitem(), vendor) for ctrl in ctrls]
+ if vendor != 'libcamera':
+ vendors.append(vendor)
+
+ for ctrl in data['controls']:
+ ctrl = Control(*ctrl.popitem(), vendor)
+ controls.append(extend_control(ctrl, args.mode))
- data = generate_py(controls, args.mode)
+ data = {
+ 'mode': args.mode,
+ 'header': headers[args.mode],
+ 'vendors': vendors,
+ 'controls': controls,
+ }
- data = fill_template(args.template, data)
+ env = jinja2.Environment()
+ template = env.from_string(open(args.template, 'r', encoding='utf-8').read())
+ string = template.render(data)
if args.output:
- output = open(args.output, 'wb')
- output.write(data.encode('utf-8'))
+ output = open(args.output, 'w', encoding='utf-8')
+ output.write(string)
output.close()
else:
- sys.stdout.write(data)
+ sys.stdout.write(string)
return 0
diff --git a/src/py/libcamera/meson.build b/src/py/libcamera/meson.build
index 6ad2d771..596a203c 100644
--- a/src/py/libcamera/meson.build
+++ b/src/py/libcamera/meson.build
@@ -26,7 +26,7 @@ pycamera_sources = files([
'py_transform.cpp',
])
-# Generate controls
+# Generate controls and properties
gen_py_controls_template = files('py_controls_generated.cpp.in')
gen_py_controls = files('gen-py-controls.py')
@@ -38,15 +38,11 @@ pycamera_sources += custom_target('py_gen_controls',
'-t', gen_py_controls_template, '@INPUT@'],
env : py_build_env)
-# Generate properties
-
-gen_py_properties_template = files('py_properties_generated.cpp.in')
-
pycamera_sources += custom_target('py_gen_properties',
input : properties_files,
output : ['py_properties_generated.cpp'],
command : [gen_py_controls, '--mode', 'properties', '-o', '@OUTPUT@',
- '-t', gen_py_properties_template, '@INPUT@'],
+ '-t', gen_py_controls_template, '@INPUT@'],
env : py_build_env)
# Generate formats
diff --git a/src/py/libcamera/py_controls_generated.cpp.in b/src/py/libcamera/py_controls_generated.cpp.in
index 26d5a104..22a132d1 100644
--- a/src/py/libcamera/py_controls_generated.cpp.in
+++ b/src/py/libcamera/py_controls_generated.cpp.in
@@ -2,12 +2,12 @@
/*
* Copyright (C) 2022, Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
*
- * Python bindings - Auto-generated controls
+ * Python bindings - Auto-generated {{mode}}
*
* This file is auto-generated. Do not edit.
*/
-#include <libcamera/control_ids.h>
+#include <libcamera/{{header}}>
#include <pybind11/pybind11.h>
@@ -15,16 +15,33 @@
namespace py = pybind11;
-class PyControls
+class Py{{mode|capitalize}}
{
};
-${vendors_class_def}
-
-void init_py_controls_generated(py::module& m)
+{% for vendor in vendors -%}
+class Py{{vendor|capitalize}}{{mode|capitalize}}
{
- auto controls = py::class_<PyControls>(m, "controls");
-${vendors_defs}
+};
-${controls}
+{% endfor -%}
+
+void init_py_{{mode}}_generated(py::module& m)
+{
+ auto {{mode}} = py::class_<Py{{mode|capitalize}}>(m, "{{mode}}");
+{%- for vendor in vendors %}
+ auto {{vendor}} = py::class_<Py{{vendor|capitalize}}{{mode|capitalize}}>({{mode}}, "{{vendor}}");
+{%- endfor %}
+
+{% for ctrl in controls %}
+ {{ctrl.klass}}.def_readonly_static("{{ctrl.name}}", static_cast<const libcamera::ControlId *>(&libcamera::{{mode}}::{{ctrl.namespace}}{{ctrl.name}}));
+{%- if ctrl.is_enum %}
+
+ py::enum_<libcamera::{{mode}}::{{ctrl.namespace}}{{ctrl.name}}Enum>({{ctrl.klass}}, "{{ctrl.name}}Enum")
+{%- for enum in ctrl.enum_values %}
+ .value("{{enum.py_name}}", libcamera::{{mode}}::{{ctrl.namespace}}{{enum.name}})
+{%- endfor %}
+ ;
+{%- endif %}
+{% endfor -%}
}
diff --git a/src/py/libcamera/py_properties_generated.cpp.in b/src/py/libcamera/py_properties_generated.cpp.in
deleted file mode 100644
index d28f1ab8..00000000
--- a/src/py/libcamera/py_properties_generated.cpp.in
+++ /dev/null
@@ -1,30 +0,0 @@
-/* SPDX-License-Identifier: LGPL-2.1-or-later */
-/*
- * Copyright (C) 2022, Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
- *
- * Python bindings - Auto-generated properties
- *
- * This file is auto-generated. Do not edit.
- */
-
-#include <libcamera/property_ids.h>
-
-#include <pybind11/pybind11.h>
-
-#include "py_main.h"
-
-namespace py = pybind11;
-
-class PyProperties
-{
-};
-
-${vendors_class_def}
-
-void init_py_properties_generated(py::module& m)
-{
- auto controls = py::class_<PyProperties>(m, "properties");
-${vendors_defs}
-
-${controls}
-}