Qube Render Manager Python API



  • At the animation studio where I work we use Qube, a render management client/server solution, that controls our render farm as well as most of the idle workstations on the floor.  The software itself works fairly well and I have no complaints about that.  But their Python API is another story.  The API actually spans several languages including C++, Perl, Python and their command line job submission tool as well, so the fact that these WTFs exist is understandable since it was probably adapted verbatim from one of the other language's coding styles.

    The laundry list beings with all classes inheriting from the dict type, rather than the object type.  Normally, if you're going to extend a container type such as a list or dictionary, you do so to add new functionality; an order dictionary or a unique-values-only list, for example.  In this case, they're simply using the dictionary to store all the different attributes of render jobs, worker processes, etc.  So rather than being able to go:

    job = Job()
    job.name = "testing"

    you have to go:

    job["name"] = "testing"

    With this being the case, why didn't they just use a plain dictionary?  Why go through all the trouble of defining a class just to make it act exactly like a dictionary?  My only guess is that they wanted some kind of type checking ability, but the only type checking they ever do is for None (null).  Another possibility is listed right in the source, as one of the comments suggests that the dictionary inheritance was added quite recently to give better attribute checking abilities like get() and default() to the existing classes.

    What's worse is they actually DO have attributes creates for every single parameter a render Job, Worker, etc. can have.  The double-worse part is every one of these attributes is a function.  This means that setting a value on the attribute the way you'd think you could like so:

    job.name = "testing"

    will actually replace the function with a string. The proper way to use this "convenience" function is:

    job.name("testing")

    Even stranger still is that every single one of these functions points to a single function called getOrSetMethod() which is some legacy thing to "easily" get or set a value through this single function based on the number of arguements supplied.  I've suggested to their API developer that they should use proper Python properties for this. Even if they are just pointing directly to the internal dictionary, at least you won't be able to accidentally overwrite the function.

    Because the classes basically act like structures/dictionaries, they have no actual work-related methods built into them.  For example, to submit a Job, you have to call a module-level submit() function, rather than just job.submit().  It would make sense to have it at the module level if anything else other than a Job could be submitted, but nothing else can.

    Continuing this trend of "class as a structure", the "factory" function in the module jobinfo() must be recalled every time you want more infomation about a Job.  You can't ask the job to refresh itself, you can't ask the job to fetch its own agenda.  And each time this function is recalled, it generates a new job object.  So you'd better make sure you turn all the flags on, otherwise you'll be getting an incomplete "light" object, or list of objects, back when you call it.

    Its also very easy to submit a bad Job that will throw an error on the queue when it tries to render it.  Its as easy as instantiating the Job class and then immediately submitting it.  The class has no minimum requirements in its constructor, even though the render queue obviously does.

    There's more, but I don't remember them all off hand at the moment.

    The good news is, their API developer, who I'm in talks with, seems genuinely interested in making the API more Pythonic.  His main concern is about breaking backwards compatibility, so hopefully the fact that all the changes I've suggested don't break anything will help improve things.



  • Looks as Perl interface ported directly to Python. In Perl objects are based on hashes where they store the attributes and there's no way to define data attributes, only methods.

    TRWTF is they are not using SWIG -- it's a tool that takes C++ object interface with a bit of extra annotations and generates appropriate binding for it in most languages you can think of. Would save quite a bit of work both in implementing and debugging (because the other interfaces would just call into the C++ one, so if a bug is found, fixing it in C++ would propagate to the other language bindings too) while allowing to add Java, C#, Ruby, Tcl/Tk and Ocaml (and some others).


Log in to reply