[Bf-taskforce25] RNA and API wrap-up

Brecht Van Lommel brecht at blender.org
Mon Jun 29 15:02:17 CEST 2009


Hi Elia,

On Mon, 2009-06-29 at 14:06 +0200, Elia Sarti wrote:
> Operators are more or less defined/complete at least C side. API side 
> though we still have things undefined or problematic. For instance an 
> issue is unique naming. Although the current naming convention fits 
> perfectly and it's cohesive on our core application side it presents 
> some limitations for extensibility as we cannot really control how many 
> pyoperators will be written and handle collisions between these. A way 
> could be simply to always prepend pyops with PY_ (or tell py devs to use 
> this convention). We could tell devs to prepend their pyoperators with 
> their own names/ids of choice. Or we could use python own module/package 
> system to combine the final operator name. As of now I don't seem to see 
> any check on operator names? We could allow a 
> module.OBJECT_OT_do_something() type of name syntax which could be 
> translated automatically from py? Not sure if this conflicts with something.

I think a convention for prefixes would be best here, don't think a
more advanced solution is needed.

> Another issue for operators in general is context constraints. For 
> instance as of now as Ton mentioned in IRC add_object operator works in 
> node editor. Is this fine? Can it be expected by the user? From my point 
> of view being able to run operators from contexts you wouldn't expect is 
> totally acceptable architecture-wise as running constraints are defined 
> in terms of "needs" and if requirements are respected I see no reason 
> why the operator should not run. They should just be hidden from the 
> user in order not to create confusion. I think we could do this with 
> some type of "flag" both on operators and space types defining the 
> constraints of context. Something like CTX_HANDLE_OBJECT, 
> CTX_HANDLE_NODE etc. On the operator this is the same as represented by 
> the prefix of its idname so we could actually use this on spaces 
> although string comparison would introduce some issues but would be more 
> solid e.g. for py/plugin spaces which brings me back to pyops naming as 
> using this method would leave module/package approach as the only usable 
> naming convention.
> You might wonder why not to put the space check directly on the 
> operator's poll? Well the reason for this is that as I said poll() 
> should just check requirements and not interface issues which is instead 
> something spaces take care of. This is not just a design choice but also 
> has some practical examples. For instance, the same example I made on 
> IRC, suppose at some point Outliner space gets capacity of 
> listing/handling nodes. To support node tools in outliner you would have 
> to go and change every single node operator to add additional check on 
> space type while with the former approach you just update outliner space 
> to accept NODE_* operators. This of course is even more clear for 
> python/plugin spaces.

I think this is what keymaps are already for. If it is useful to share
some set of operators between spaces, they can be added in a keymap and
both spaces can add that keymap in.

> Type registration
> 
> This is less important and more of a personal question than general issue.
> Types can get registered for adding customization like it happens for 
> Panels or Menus. If I'm not mistaken this is how current architecture works:
> 
> script.py -> class SomePanel(Panel): [...] bpy.types.register(SomePanel) ->
> 
>     RNA: Panel.register() -> register the type in some custom way -> 
> (ARegionType.paneltypes)
>         
>         ARegionType.draw() ->
>         
>             ED_region_panels("context") ->
>             
>                 foreach (panel in paneltypes) if (panel.context == 
> "context") draw(panel)
>                 
> 
> How it should work in my opinion:
> 
> script.py -> class SomePanel(Panel): [...] bpy.types.register(SomePanel) ->
> 
>     [RNA: insert SomePanel in optimized lookup list]
>         
>         ARegionType.init() ->
>             
>             retrieve all Panel-inherited RNA types and register -> 
> (ARegionType.autopaneltypes)
>         
>         ARegionType.draw() ->
>         
>             ED_region_panels("context") ->
>             
>                 foreach (panel in paneltypes) draw(panel)
>                 foreach (panel in autopaneltypes) if (panel.context == 
> "context") draw(panel)

The reason I do it in the first way is because then there is no
distinction between paneltypes and autopaneltypes, for the panel drawing
code it's all the same. For panels this is not a big problem because
they are only looped over in one place, but I think it is important that
from the C code using them, they look 100% identical.

> Advantages of this kind of architecture are that it keeps RNA more 
> consistent and self-contained, no messing around (i.e. no real logics) 
> with interface. By consequence it also keeps interface stuff more 
> self-contained by letting it handle its own supported custom types.

I don't understand this argument, if the interface code needs to be kept
together more, it's just a matter of making the register function in RNA
call some function in the interface code.

> The apparent disadvantage is low performance, having to do lookups at 
> every init. I understand this could be why the first method was adopted 
> but to me this doesn't seem a valid reason. To solve this problem 
> instead we could create a coexistent fast-to-access list of registered 
> types. This could be ordered like some type of tree to optimize access 
> based on type hierarchy lookup. I know caches are bad but still I think 
> they're better than code fragmentation. Once we have this type of cache 
> we could further optimize for instance like (in pseudo-code):
> 
> if type_tree.is_hierarchy_changed_for(Panel):
>     re-init ARegionType.autopaneltypes
> 
> This could simply be a flag on the cache-stored type updated by the 
> cache itself. This way we would re-do the lookup only when some new 
> specific type gets registered/unregistered.

I didn't choose this method for performance, it just seemed to be the
simplest way to do it.

I don't understand how introducing autopaneltypes helps, it only makes
it more complicated in my opinion. The register function needs to check
validness, override existing panels with the same idname, etc. That way
it can throw an error to python immediately. Splitting up validation and
actual registration means you split up the code in two parts.

But there is another problem, when you want to register a spacetype,
with regions in that spacetype, and panels in those regions. Then you
can't delay re-init for spacetypes and regions immediately anyway.

Generally I think it's more predictable and easier to debug doing
things immediately instead of delayed.

> I think I mentioned the main issues that must be either solved, decided 
> upon or simply settled, if you think I forgot something feel free to add it.
> I'm also thinking about writing some more detailed docs on RNA and its 
> internals in order not to turn it into a black box that only a few 
> understand. This of course with the assistance of Brecht.

Yes, we definitely need more docs.

Brecht.




More information about the Bf-taskforce25 mailing list