Decorating your Python code

October 10th, 2008 by Patrick Boucher - Viewed 16065 times -




I have touched on Python decorators in the past in this article (although that decorator was a bit convoluted). Decorators can be extremely useful in many situations and here are a few that I propose may help streamline script development.

Note: For those of you who would like a bit of background information on decorators you can check out this page.

Keeping things quiet

Let’s look at the code and we’ll go for a few explanations afterward.

1
2
3
4
5
6
7
8
9
10
11
def suspendXSILogging(func):
    def closure(*args, **kwargs):
        logPrefs = getXSILoggingPrefs()
        try:
            ret = func(*args, **kwargs)
        except Exception, e:
            setXSILoggingPrefs(logPrefs)
            raise
        setXSILoggingPrefs(logPrefs)
        return ret
    return closure

The first function suspendXSILogging can be used as a decorator and will disable all logging features of XSI. This has the advantage of making large scripts that may be command dependent a bit quicker.

You could always implement a plugin with a custom command but sometimes a script is simpler. Even in the command context this decorator could be useful if you wanted part of the command to log to the script editor while keeping other parts silent.

Another advantage of this decorator is that it puts your original function in a large trap that, if an error should occur, will make sure that logging is reset to the users original preferences.

Time for decorating

1
2
3
4
5
6
7
8
9
10
11
12
13
def timeExecution(func):
    def closure(*args, **kwargs):
        startTime = time.time()
        try:
            ret = func(*args, **kwargs)
        except Exception, e:
            delta = time.time() - startTime
            log.error('Failed in %f seconds' % delta)
            raise
        delta = time.time() - startTime
        log.info('Finished in %f seconds' % delta)
        return ret
    return closure

This second function, timeExecution, can decorate almost any function and will print to the script editor the amount of time the decorated function took to execute. This can be used as a debugging or performance tracking tool or as a way to provide feedback to your users. Again, a trap is implemented that will log execution time even if an error occurs.

Please not that this decorator uses a logging system described here. If you would rather use simple logging you should replace log.info( instances by log(, xsi.LogMessage( or Application.LogMessage( depending on your scripting habits.

Usage example

1
2
3
4
5
@timeExecution
@suspendXSILogging
def main():
    # Do some fun useful stuff - well... more useful than this!
    xsi.CreatePrim("Cube", "MeshSurface", "", "")

You can chain decorators without hesitation like in this example, one thing to note in this particular case is that you’ll want timeExecution higher in the decorator chain than suspendXSILogging otherwise the time information will never make it to the script editor. Duh!

Support code

Before I sign off on this latest article, here are two functions that, if useful on their own, are essential to the functioning of the suspendXSILogging decorator.

1
2
3
4
5
6
7
8
9
10
11
def getXSILoggingPrefs():
    prefs = xsi.Preferences
    vals = {}
    for n in ['scripting.cmdlog', 'scripting.msglog', 'scripting.msglogverbose']:
        vals[n] = prefs.GetPreferenceValue(n)
        prefs.SetPreferenceValue(n, False)
    return vals
 
def setXSILoggingPrefs(vals):
    for key, val in vals.iteritems():
        xsi.Preferences.SetPreferenceValue(key, val)

Cheers!

One Response to “Decorating your Python code”

  1. Alok Gandhi says:

    Hi Patrick,

    Great read. Looking forward to using the decorative is some very useful and efficieny-boosting way in the current tool I am developing.

    Thanks