[09/10] py: gen-py-controls: Use Control class
diff mbox series

Message ID 20240809005914.20662-10-laurent.pinchart@ideasonboard.com
State Accepted
Headers show
Series
  • libcamera: Improve code generation for controls
Related show

Commit Message

Laurent Pinchart Aug. 9, 2024, 12:59 a.m. UTC
Replace manual extraction of data from YAML with the Control helper
class. This centralizes YAML parsing and avoids manual mistakes.

In order to import the controls module, add the utils/codegen/ directory
to the PYTHONPATH through the Python build environment.

Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
---
 src/py/libcamera/gen-py-controls.py | 32 ++++++++++++++---------------
 src/py/libcamera/meson.build        |  6 ++++--
 utils/codegen/meson.build           |  1 +
 3 files changed, 21 insertions(+), 18 deletions(-)

Comments

Tomi Valkeinen Aug. 12, 2024, 2:33 p.m. UTC | #1
On 09/08/2024 03:59, Laurent Pinchart wrote:
> Replace manual extraction of data from YAML with the Control helper
> class. This centralizes YAML parsing and avoids manual mistakes.
> 
> In order to import the controls module, add the utils/codegen/ directory
> to the PYTHONPATH through the Python build environment.
> 
> Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
> ---
>   src/py/libcamera/gen-py-controls.py | 32 ++++++++++++++---------------
>   src/py/libcamera/meson.build        |  6 ++++--
>   utils/codegen/meson.build           |  1 +
>   3 files changed, 21 insertions(+), 18 deletions(-)
> 
> diff --git a/src/py/libcamera/gen-py-controls.py b/src/py/libcamera/gen-py-controls.py
> index 8efbf95b9433..a18dc5337090 100755
> --- a/src/py/libcamera/gen-py-controls.py
> +++ b/src/py/libcamera/gen-py-controls.py
> @@ -8,6 +8,8 @@ import string
>   import sys
>   import yaml
>   
> +from controls import Control
> +
>   
>   def find_common_prefix(strings):
>       prefix = strings[0]
> @@ -28,9 +30,7 @@ def generate_py(controls, mode):
>       vendor_defs = []
>       vendors = []
>       for vendor, ctrl_list in controls.items():
> -        for ctrls in ctrl_list:
> -            name, ctrl = ctrls.popitem()
> -
> +        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))
> @@ -44,29 +44,28 @@ def generate_py(controls, mode):
>                   ns = 'libcamera::{}::'.format(mode)
>                   container = 'controls'
>   
> -            out += f'\t{container}.def_readonly_static("{name}", static_cast<const libcamera::ControlId *>(&{ns}{name}));\n\n'
> +            out += f'\t{container}.def_readonly_static("{ctrl.name}", static_cast<const libcamera::ControlId *>(&{ns}{ctrl.name}));\n\n'
>   
> -            enum = ctrl.get('enum')
> -            if not enum:
> +            if not ctrl.is_enum:
>                   continue
>   
> -            cpp_enum = name + 'Enum'
> +            cpp_enum = ctrl.name + 'Enum'
>   
>               out += '\tpy::enum_<{}{}>({}, \"{}\")\n'.format(ns, cpp_enum, container, cpp_enum)
>   
>               if mode == 'controls':
>                   # Adjustments for controls
> -                if name == 'LensShadingMapMode':
> +                if ctrl.name == 'LensShadingMapMode':
>                       prefix = 'LensShadingMapMode'
>                   else:
> -                    prefix = find_common_prefix([e['name'] for e in enum])
> +                    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 enum])
> +                prefix = find_common_prefix([e.name for e in ctrl.enum_values])
>   
> -            for entry in enum:
> -                cpp_enum = entry['name']
> -                py_enum = entry['name'][len(prefix):]
> +            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)
>   
> @@ -103,9 +102,10 @@ def main(argv):
>   
>       controls = {}
>       for input in args.input:
> -        data = open(input, 'rb').read()
> -        vendor = yaml.safe_load(data)['vendor']
> -        controls[vendor] = yaml.safe_load(data)['controls']
> +        data = yaml.safe_load(open(input, 'rb').read())
> +        vendor = data['vendor']
> +        ctrls = data['controls']
> +        controls[vendor] = [Control(*ctrl.popitem(), vendor) for ctrl in ctrls]
>   
>       data = generate_py(controls, args.mode)
>   
> diff --git a/src/py/libcamera/meson.build b/src/py/libcamera/meson.build
> index 2e67407598db..6ad2d7713e4d 100644
> --- a/src/py/libcamera/meson.build
> +++ b/src/py/libcamera/meson.build
> @@ -35,7 +35,8 @@ pycamera_sources += custom_target('py_gen_controls',
>                                     input : controls_files,
>                                     output : ['py_controls_generated.cpp'],
>                                     command : [gen_py_controls, '--mode', 'controls', '-o', '@OUTPUT@',
> -                                             '-t', gen_py_controls_template, '@INPUT@'])
> +                                             '-t', gen_py_controls_template, '@INPUT@'],
> +                                  env : py_build_env)
>   
>   # Generate properties
>   
> @@ -45,7 +46,8 @@ 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_properties_template, '@INPUT@'],
> +                                  env : py_build_env)
>   
>   # Generate formats
>   
> diff --git a/utils/codegen/meson.build b/utils/codegen/meson.build
> index fb2196ee0d20..adf33bbab9e1 100644
> --- a/utils/codegen/meson.build
> +++ b/utils/codegen/meson.build
> @@ -5,6 +5,7 @@
>   py_build_env = environment()
>   # \todo Investigate usage of PYTHONPYCACHEPREFIX for Python >= 3.8
>   py_build_env.set('PYTHONDONTWRITEBYTECODE', '1')
> +py_build_env.prepend('PYTHONPATH', meson.current_source_dir())
>   
>   py_modules += ['jinja2', 'yaml']
>   

Reviewed-by: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>

  Tomi
Paul Elder Aug. 15, 2024, 4:59 a.m. UTC | #2
On Fri, Aug 09, 2024 at 03:59:13AM +0300, Laurent Pinchart wrote:
> Replace manual extraction of data from YAML with the Control helper
> class. This centralizes YAML parsing and avoids manual mistakes.
> 
> In order to import the controls module, add the utils/codegen/ directory
> to the PYTHONPATH through the Python build environment.
> 
> Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>

Reviewed-by: Paul Elder <paul.elder@ideasonboard.com>

> ---
>  src/py/libcamera/gen-py-controls.py | 32 ++++++++++++++---------------
>  src/py/libcamera/meson.build        |  6 ++++--
>  utils/codegen/meson.build           |  1 +
>  3 files changed, 21 insertions(+), 18 deletions(-)
> 
> diff --git a/src/py/libcamera/gen-py-controls.py b/src/py/libcamera/gen-py-controls.py
> index 8efbf95b9433..a18dc5337090 100755
> --- a/src/py/libcamera/gen-py-controls.py
> +++ b/src/py/libcamera/gen-py-controls.py
> @@ -8,6 +8,8 @@ import string
>  import sys
>  import yaml
>  
> +from controls import Control
> +
>  
>  def find_common_prefix(strings):
>      prefix = strings[0]
> @@ -28,9 +30,7 @@ def generate_py(controls, mode):
>      vendor_defs = []
>      vendors = []
>      for vendor, ctrl_list in controls.items():
> -        for ctrls in ctrl_list:
> -            name, ctrl = ctrls.popitem()
> -
> +        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))
> @@ -44,29 +44,28 @@ def generate_py(controls, mode):
>                  ns = 'libcamera::{}::'.format(mode)
>                  container = 'controls'
>  
> -            out += f'\t{container}.def_readonly_static("{name}", static_cast<const libcamera::ControlId *>(&{ns}{name}));\n\n'
> +            out += f'\t{container}.def_readonly_static("{ctrl.name}", static_cast<const libcamera::ControlId *>(&{ns}{ctrl.name}));\n\n'
>  
> -            enum = ctrl.get('enum')
> -            if not enum:
> +            if not ctrl.is_enum:
>                  continue
>  
> -            cpp_enum = name + 'Enum'
> +            cpp_enum = ctrl.name + 'Enum'
>  
>              out += '\tpy::enum_<{}{}>({}, \"{}\")\n'.format(ns, cpp_enum, container, cpp_enum)
>  
>              if mode == 'controls':
>                  # Adjustments for controls
> -                if name == 'LensShadingMapMode':
> +                if ctrl.name == 'LensShadingMapMode':
>                      prefix = 'LensShadingMapMode'
>                  else:
> -                    prefix = find_common_prefix([e['name'] for e in enum])
> +                    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 enum])
> +                prefix = find_common_prefix([e.name for e in ctrl.enum_values])
>  
> -            for entry in enum:
> -                cpp_enum = entry['name']
> -                py_enum = entry['name'][len(prefix):]
> +            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)
>  
> @@ -103,9 +102,10 @@ def main(argv):
>  
>      controls = {}
>      for input in args.input:
> -        data = open(input, 'rb').read()
> -        vendor = yaml.safe_load(data)['vendor']
> -        controls[vendor] = yaml.safe_load(data)['controls']
> +        data = yaml.safe_load(open(input, 'rb').read())
> +        vendor = data['vendor']
> +        ctrls = data['controls']
> +        controls[vendor] = [Control(*ctrl.popitem(), vendor) for ctrl in ctrls]
>  
>      data = generate_py(controls, args.mode)
>  
> diff --git a/src/py/libcamera/meson.build b/src/py/libcamera/meson.build
> index 2e67407598db..6ad2d7713e4d 100644
> --- a/src/py/libcamera/meson.build
> +++ b/src/py/libcamera/meson.build
> @@ -35,7 +35,8 @@ pycamera_sources += custom_target('py_gen_controls',
>                                    input : controls_files,
>                                    output : ['py_controls_generated.cpp'],
>                                    command : [gen_py_controls, '--mode', 'controls', '-o', '@OUTPUT@',
> -                                             '-t', gen_py_controls_template, '@INPUT@'])
> +                                             '-t', gen_py_controls_template, '@INPUT@'],
> +                                  env : py_build_env)
>  
>  # Generate properties
>  
> @@ -45,7 +46,8 @@ 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_properties_template, '@INPUT@'],
> +                                  env : py_build_env)
>  
>  # Generate formats
>  
> diff --git a/utils/codegen/meson.build b/utils/codegen/meson.build
> index fb2196ee0d20..adf33bbab9e1 100644
> --- a/utils/codegen/meson.build
> +++ b/utils/codegen/meson.build
> @@ -5,6 +5,7 @@
>  py_build_env = environment()
>  # \todo Investigate usage of PYTHONPYCACHEPREFIX for Python >= 3.8
>  py_build_env.set('PYTHONDONTWRITEBYTECODE', '1')
> +py_build_env.prepend('PYTHONPATH', meson.current_source_dir())
>  
>  py_modules += ['jinja2', 'yaml']
>

Patch
diff mbox series

diff --git a/src/py/libcamera/gen-py-controls.py b/src/py/libcamera/gen-py-controls.py
index 8efbf95b9433..a18dc5337090 100755
--- a/src/py/libcamera/gen-py-controls.py
+++ b/src/py/libcamera/gen-py-controls.py
@@ -8,6 +8,8 @@  import string
 import sys
 import yaml
 
+from controls import Control
+
 
 def find_common_prefix(strings):
     prefix = strings[0]
@@ -28,9 +30,7 @@  def generate_py(controls, mode):
     vendor_defs = []
     vendors = []
     for vendor, ctrl_list in controls.items():
-        for ctrls in ctrl_list:
-            name, ctrl = ctrls.popitem()
-
+        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))
@@ -44,29 +44,28 @@  def generate_py(controls, mode):
                 ns = 'libcamera::{}::'.format(mode)
                 container = 'controls'
 
-            out += f'\t{container}.def_readonly_static("{name}", static_cast<const libcamera::ControlId *>(&{ns}{name}));\n\n'
+            out += f'\t{container}.def_readonly_static("{ctrl.name}", static_cast<const libcamera::ControlId *>(&{ns}{ctrl.name}));\n\n'
 
-            enum = ctrl.get('enum')
-            if not enum:
+            if not ctrl.is_enum:
                 continue
 
-            cpp_enum = name + 'Enum'
+            cpp_enum = ctrl.name + 'Enum'
 
             out += '\tpy::enum_<{}{}>({}, \"{}\")\n'.format(ns, cpp_enum, container, cpp_enum)
 
             if mode == 'controls':
                 # Adjustments for controls
-                if name == 'LensShadingMapMode':
+                if ctrl.name == 'LensShadingMapMode':
                     prefix = 'LensShadingMapMode'
                 else:
-                    prefix = find_common_prefix([e['name'] for e in enum])
+                    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 enum])
+                prefix = find_common_prefix([e.name for e in ctrl.enum_values])
 
-            for entry in enum:
-                cpp_enum = entry['name']
-                py_enum = entry['name'][len(prefix):]
+            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)
 
@@ -103,9 +102,10 @@  def main(argv):
 
     controls = {}
     for input in args.input:
-        data = open(input, 'rb').read()
-        vendor = yaml.safe_load(data)['vendor']
-        controls[vendor] = yaml.safe_load(data)['controls']
+        data = yaml.safe_load(open(input, 'rb').read())
+        vendor = data['vendor']
+        ctrls = data['controls']
+        controls[vendor] = [Control(*ctrl.popitem(), vendor) for ctrl in ctrls]
 
     data = generate_py(controls, args.mode)
 
diff --git a/src/py/libcamera/meson.build b/src/py/libcamera/meson.build
index 2e67407598db..6ad2d7713e4d 100644
--- a/src/py/libcamera/meson.build
+++ b/src/py/libcamera/meson.build
@@ -35,7 +35,8 @@  pycamera_sources += custom_target('py_gen_controls',
                                   input : controls_files,
                                   output : ['py_controls_generated.cpp'],
                                   command : [gen_py_controls, '--mode', 'controls', '-o', '@OUTPUT@',
-                                             '-t', gen_py_controls_template, '@INPUT@'])
+                                             '-t', gen_py_controls_template, '@INPUT@'],
+                                  env : py_build_env)
 
 # Generate properties
 
@@ -45,7 +46,8 @@  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_properties_template, '@INPUT@'],
+                                  env : py_build_env)
 
 # Generate formats
 
diff --git a/utils/codegen/meson.build b/utils/codegen/meson.build
index fb2196ee0d20..adf33bbab9e1 100644
--- a/utils/codegen/meson.build
+++ b/utils/codegen/meson.build
@@ -5,6 +5,7 @@ 
 py_build_env = environment()
 # \todo Investigate usage of PYTHONPYCACHEPREFIX for Python >= 3.8
 py_build_env.set('PYTHONDONTWRITEBYTECODE', '1')
+py_build_env.prepend('PYTHONPATH', meson.current_source_dir())
 
 py_modules += ['jinja2', 'yaml']