[Bf-python] 2nd thoughts on the node api

Toni Alatalo antont at kyperjokki.fi
Sun Dec 10 15:41:33 CET 2006


i'm having second thoughts on the node api. after all, we all know that 
things should be as simple as possible (but not simpler).

initially, during Orange when Ton was making the node system, i was 
thinking that nodes are functions: they take input, and give output, 
based on the processing inside. so was sketching something like:

def mynode(color, amount):
    image = x(color)(y(amount)z(random))
    return image
    
nice, huh?

the problem there is, though, that i don't know if Python function 
signatures can be introspected, for Blender to be able to know the node 
definition. so, as Python has the nice way of having information about 
functions by making them with classes, the follow-up was something like:

class MyNode(Blender.Node):
    inputs = {'color': RGBA, 'amount': NUMBER}
    outputs = [IMAGE]
    
    def __call__(self):
        image = x(self.color)y(self.amount)(z(random))
        return image
        
not bad? so this is what i was advocating when Jestie took up the job, 
and basically this is what we have now, and i guess it is really cool. 
the concerns from some scripters about the complexity -- need to have a 
class, use the obscure __call__ method etc -- we have silenced with the 
idea that there would be GUI in the script node that takes care of that: 
you add inputs & outputs by clicking new sockets with the mouse, and 
then there is a script editing thing where you just put your function, 
which is then put to __call__ behind the scenes and you don't have to 
know. that may be good, and perhaps we even get it done :)

but now i started thinking that perhaps all that is also wrong / not 
sufficient. we should not restrict scripting to such a rigid structure 
unless it is really needed, and i don't think it is. so how about going 
back to something like this, that was already implemented earlier i think?

inputs = [RGBA, NUMBER]
outputs = [IMAGE]

def xyz(color, amount):
    image = x(color)y(amount)(z(random))
    return image
    
NodeType("MyNode", inputs, outputs, xyz)
#or register_nodetype("MyNode", inputs, outputs, xyz)

there the users dont have to know about classes and magic methods, and 
the system is straightforwardly flexible for potential weird complex 
needs too, 'cause you can just get the parts of the nodedef: socket 
declarations and the processing func from anywhere and put them there. 
so now i regret / take back my arguments againts that technique -- sorry.

now, i don't know if it makes sense in practice, but at least for me as 
an academic / stupid / whatever question is whether that NodeType / 
registration thing could be just plugged in to complement the current 
implementation, where you introduce new nodetypes by implementing a 
Node. i mean just as an alternative way of declaring the own node class. 
BTW i don't know if the c implementation actually uses the inheritance 
mechanism there, or whether it would actually be just better to use duck 
typing and not require the inheritance - a node could be of any class 
that provides the node interface. but if the registration and even 
execution mechanism uses inheritance somehow i guess it is good. but 
back to the esoteric question: can we make class factories easily in 
Python? something like:

def register_nodetype(name, inputs, outputs, impl):
    nodetype = type(..)
    #here my knowledge ends
    nodetype.__init__ .. should somehow use the given input&output declrs
    nodetype.__call__ = impl #i have no idea if
    
class type(object)
 |  type(name, bases, dict) -> a new type

but it seems i still don't understand what types and classes are in python:
In [43]: t = type("MyNode", (Node), {})
TypeError: type() argument 2 must be tuple, not classobj

t = type("MyNode", (), {})

works but then i don't know what to do :)

am writing this offline so did not read any docus - may later, but am 
not at all surprised if that that type creation magic is not needed, but 
the users-dont-need-to-know-classes way can be implemented on the c side 
without such trickery too. was just having a bit of fun with that 
thought, and would still like to know how to make such class factories :)

ah - am sorry for the long writeup, but now i finally realized that 
classes are just objects too and you can put the declarations anywhere:

In [46]: def c():
   ....:     class C:
   ....:         def __init__(self):
   ....:             print "yo!"
   ....:     return C
In [47]: k = c()
In [48]: k()
yo!
Out[48]: <__main__.C instance at 0x00EC5D78>

so perhaps a node class factory can be made straightforwardly with that 
.. yes, that seems to work! gotta love Python :)

def make_nodetype(name, inp, out, impl):
    class NewNode(Blender.Node):
        inputs = inp
        outputs = out
        __call__ = impl
    return NewNode
    
an executable example with a self-test is attached and on-line at 
http://an.org/blender/nodefactory.py - it creates a new nodetype using 
that class factory function, instanciates one such node, and executes it 
(does 2*3 ==> 6 :)

so should we just add such a utility func as an alternative node 
registration way? (for people who dont want to know about classes) .. i 
just hope that the c impl allows such a trick. one question is the class 
name - dunno how it uses that actually, and whether it could be set 
(with the type() function it seemed you can..)

a counterargument could be that an API should provide only one way of 
doing things and support that, but i think that point is moot here: if 
that works, it is just a different way of doing the same thing, and 
libraries often have utility functions as alternative interfaces to the 
same underlying things that can be used directly too.

~Toni




More information about the Bf-python mailing list