Description
Currently there are situations where it's not possible to create arguments to pass into functions without first creating data-blocks elsewhere (typically in G.main).
While this is not an urgent problem, there are situations where this is inconvenient.
Motivation
In some cases it's useful to be able to pass in a DNA-struct
two examples of this are:
- To construct an ImageUser to pass to bpy.types.Image.gl_load()
Currently we would either need to expand all image user arguments to construct an ImageUser in the gl_load function.
- To construct key-map properties to pass to wm.keyconfigs.find_item_from_operator().
Currently release/scripts/modules/bl_keymap_utils/keymap_from_toolbar.py creates a key-map item so it's possible to create properties.
EDIT: would likely be a special case (even in the context of this topic) as it's type depends on another argument that is passed to the function. It may be that this requires custom parsing logic in the C/Python API.
Solutions
Here are some solutions we could consider to address this limitation:
Support Coerce Dictionaries
This would allow a dictionary to be passed as arguments to RNA functions.
An example of how this could work:
image.gl_load(image_user=dict(
frame_current=5,
multilayer_view=2,
multilayer_pass=1,
))Details:
- Values that are unset would use DNA defaults.
- An argument flag would be used to specify coercing is supported (to prevent this being used in situations where a reference to the memory passed in would be kept.)
- Any unknown arguments or invalid types would raise an exception.
- ID-properties could be supported as well as dictionaries (if we want).
Pros:
- Straightforward to implement and understand.
- Doesn't complicate memory management, data creation and free can be done in the scope of a single function.
Cons:
- Not very Pythonic (see other possible solution).
- Slower in situations where the same argument is passed in many times.
Support Creating Instanced of RNA Types
We could support creating data from Python which is not part of Blender's data-base (G.main).
Since this opens ourselves up for complications regarding data ownership I'd propose only to support this for specific types.
So it should not be possible to create an ID and assign it to object-data.
Types could be created using their types, e.g.
image_user = bpy.type.ImageUser() image_user.frame_current = 5 image_user.multilayer_view = 2 image_user.multilayer_pass = 1
Details:
- The lifetime of these types would be bound to the Python-object.
- The Python API would need to check this kind of data is never assigned or passed to functions that hold references to the memory.
Pros:
- Convenient, Pythonic.
Cons:
- Complicates the Python API & memory management for a corner case.
- Types that start simple may be extended later in ways that complicate the API (for example if the image-user points to other ID data-blocks), meaning we would have to handle user-counts differently for data that Python-owned memory references.
A context manager could be used to limit the scope of Python owned data, however this is seems awkward, especially for multiple objects. It would simplify memory management though.