Python: Decorator Classes On The Edge

OK, I cheated.

In yesterday’s post on writing decorator classes that decorate methods, I left out two edge cases that can’t be completely ignored: static methods and class methods.

To illustrate, I’ll start where I left off yesterday, adding a decorated class method and a decorated static method to the example:

import types

class DebugTrace(object):
    def __init__(self, f):
        print("Tracing: {0}".format(f.__name__))
        self.f = f

    def __get__(self, obj, ownerClass=None):
        # Return a wrapper that binds self as a method of obj (!)
        return types.MethodType(self, obj)

    def __call__(self, *args, **kwargs):
        print("Calling: {0}".format(self.f.__name__))
        return self.f(*args, **kwargs)

class Greeter(object):
    instances = 0

    def __init__(self):
        Greeter.instances += 1
        self._inst = Greeter.instances

    @DebugTrace
    def hello(self):
        print("*** Greeter {0} says hello!".format(self._inst))

    @DebugTrace
    @classmethod
    def classHello(cls, to):
        print("*** The {0} class says hello to {1}".format(cls.__name__, to))

    @DebugTrace
    @staticmethod
    def staticHello(to):
        print("*** Something says hello to " + to)

@DebugTrace
def greet():
    g = Greeter()
    g2 = Greeter()
    g.hello()
    g2.hello()
    Greeter.staticHello("you")
    Greeter.classHello("everyone")

greet()

Running this gives an error:


Tracing: hello
Traceback (most recent call last):
  File "DecoratorExample.py", line 17, in <module>
    class Greeter(object):
  File "DecoratorExample.py", line 29, in Greeter
    @classmethod
  File "DecoratorExample.py", line 5, in __init__
    print("Tracing: {0}".format(f.__name__))
AttributeError: 'classmethod' object has no attribute '__name__'

Just for this example, I’ll try removing the “Tracing” print call; but still no joy:

Calling: greet
Calling: hello
*** Greeter 1 says hello!
Calling: hello
*** Greeter 2 says hello!
Traceback (most recent call last):
  File "DecoratorExample.py", line 48, in <module>
    greet()
  File "DecoratorExample.py", line 14, in __call__
    return self.f(*args, **kwargs)
  File "DecoratorExample.py", line 45, in greet
    Greeter.staticHello("you")
  File "DecoratorExample.py", line 10, in __get__
    return types.MethodType(self, obj)
TypeError: self must not be None

The essential problem is that class methods and static methods are not callable.1 There’s an easy enough workaround: always use @staticmethod or @classmethod as the outermost (i.e., last) decorator in a sequence, as in:

    @classmethod
    @DebugTrace
    def classHello(cls, to):
        print("*** The Greeter class says hello to " + to)

    @staticmethod
    @DebugTrace
    def staticHello(to):
        print("*** Something says hello to " + to)

That produces the desired result:

Tracing: hello
Tracing: classHello
Tracing: staticHello
Tracing: greet
Calling: greet
Calling: hello
*** Greeter 1 says hello!
Calling: hello
*** Greeter 2 says hello!
Calling: staticHello
*** Something says hello to you
Calling: classHello
*** The Greeter class says hello to everyone

But suppose we really, really need to decorate an already-decorated classmethod or staticmethod. The key lies again in the descriptor protocol.

First, we need to modify the decorator’s __init__ method. (Note that the only reason that we need to modify __init__ is to find the name of the classmethod or staticmethod that’s being decorated. If we didn’t produce the “Tracing:” output, we could leave __init__ alone.)

The new __init__ method detects whether the passed “function” has a __call__ method. If it doesn’t, then it’s reasonable to assume that it’s a classmethod or a staticmethod. Calling the object’s __get__ method returns a function object, from which we can get the function name:

    def __init__(self, f):
        self.f = f
        if hasattr(f, "__call__"):
            name = self.f.__name__
        else:
            # f is a class or static method.
            tmp = f.__get__(None, f.__class__)
            name = tmp.__name__
        print("Tracing: {0}".format(name))

In the decorator’s __get__ method, we’ll know that we’re dealing with a staticmethod or classmethod if the passed obj has the value None. If that’s the case, then we make a one-time adjustment to self.f, ensuring that it points to the underlying function.

Wait—why didn’t we do this in DebugTrace.__init__? It may seem redundant, but the call to f.__get__ that we made in DebugTrace.__init__ doesn’t count: that call didn’t specify the class that f actually belongs to. (Any class works for the purpose of getting the function’s name.) Now that we’re in DebugTrace.__get__, we know via the ownerClass parameter the class that self.f is associated with. This class may make its way into a classmethod call (e.g., the call to Greeter.classHello), so it matters that we get it right.

Note that we return self in this case. We don’t want to create a new method object for classmethods or staticmethods; just calling self.__call__ will call the method appropriately.

    def __get__(self, obj, ownerClass=None):
        if obj is None:
            f = self.f
            if not hasattr(f, "__call__"):
                self.f = f.__get__(None, ownerClass)
            return self
        else:
            # Return a wrapper that binds self as a method of obj (!)
            return types.MethodType(self, obj)
Setting self.f as above might raise thread-safety issues, especially if you don’t want to rely on the atomicity of modifying a dict in-place. Borrowing from Ian Bicking’s solution, which returns a copy of the decorator for each call to __get__, can help us dodge the concurrency bullet. We’d replace

            return self

with

            return self.__class__(self.f)

However, this results in any side effects in the decorator’s __init__ method being re-executed for every call to the decorated method. Note the additional “Tracing:” lines in the output here:

Tracing: hello
Tracing: classHello
Tracing: staticHello
Tracing: greet
Calling: greet
Calling: hello
*** Greeter 1 says hello!
Calling: hello
*** Greeter 2 says hello!
Tracing: staticHello
Calling: staticHello
*** Something says hello to you
Tracing: classHello
Calling: classHello
*** The Greeter class says hello to everyone

Another option, of course, is to use a mutex around the statement that modifies self.f.

The decorator’s __call__ method is unchanged from yesterday’s example. As before, it simply prints out the desired trace message, then invokes self.f.

Here’s the entire decorator, as revised:

import types

class DebugTrace(object):
    def __init__(self, f):
        self.f = f
        if hasattr(f, "__call__"):
            name = self.f.__name__
        else:
            # f is a class or static method
            tmp = f.__get__(None, f.__class__)
            name = tmp.__name__
        print("Tracing: {0}".format(name))

    def __get__(self, obj, ownerClass=None):
        if obj is None:
            f = self.f
            if not hasattr(f, "__call__"):
                self.f = f.__get__(None, ownerClass)
            return self
        else:
            # Return a wrapper that binds self as a method of obj (!)
            return types.MethodType(self, obj)

    def __call__(self, *args, **kwargs):
        print("Calling: {0}".format(self.f.__name__))
        return self.f(*args, **kwargs)

class Greeter(object):
    instances = 0

    def __init__(self):
        Greeter.instances += 1
        self._inst = Greeter.instances

    @DebugTrace
    def hello(self):
        print("*** Greeter {0} says hello!".format(self._inst))

    @DebugTrace
    @classmethod
    def classHello(cls, to):
        print("*** The {0} class says hello to {1}".format(cls.__name__, to))

    @DebugTrace
    @staticmethod
    def staticHello(to):
        print("*** Something says hello to " + to)

@DebugTrace
def greet():
    g = Greeter()
    g2 = Greeter()
    g.hello()
    g2.hello()
    Greeter.staticHello("you")
    Greeter.classHello("everyone")

greet()

I’ve tested this with Python 2.6, 2.7, and 3.1.


1 Without taking a deep dive into Python’s history, I couldn’t say why they’re not callable. But it does seem that class methods and static methods were never intended to be used frequently.

Python: Decorating with class through descriptors

Update: If you find this article helpful, you may want to read the follow-up.

As a fairly new Python developer, my first attempt at decorators hit a snag: my simple class-based decorator failed when decorating a method. I got around the immediate problem by rewriting the decorator as a function. Yet the episode left me wondering if there were some way to fix the class-based decorator to work when applied to methods. I’ve found what seems like an elegant solution, and picked up a better understanding of decorators and descriptors in the process.

Here’s an example that illustrates the original problem. DebugTrace is the decorator class:

class DebugTrace(object):
    def __init__(self, f):
        print("Tracing: {0}".format(f.__name__))
        self.f = f

    def __call__(self, *args, **kwargs):
        print("Calling: {0}".format(self.f.__name__))
        return self.f(*args, **kwargs)

class Greeter(object):
    instances = 0

    def __init__(self):
        Greeter.instances += 1
        self._inst = Greeter.instances

    @DebugTrace
    def hello(self):
        print("*** Greeter {0} says hello!".format(self._inst))

@DebugTrace
def greet():
    g = Greeter()
    g2 = Greeter()
    g.hello()
    g2.hello()

greet()

Running this with Python 2.6 or 3.1 results in an error:

Tracing: hello
Tracing: greet
Calling greet
Calling hello
Traceback (most recent call last):
  File "./DecoratorExample.py", line 31, in <module>
    greet()
  File "./DecoratorExample.py", line 8, in __call__
    return self.f(*args, **kwargs)
  File "./DecoratorExample.py", line 27, in greet
    g.hello()
  File "./DecoratorExample.py", line 8, in __call__
    return self.f(*args, **kwargs)
TypeError: hello() takes exactly 1 argument (0 given)

The output explains the problem. DebugTrace was instantiated only twice: once for Greeter.hello, and once for greet. That’s a reminder that decoration occurs during compile time, not run time. Accordingly, DebugTrace‘s reference to Greeter.hello represents an unbound function—it doesn’t reference any Greeter instances. So no ‘self’ argument was passed into the call to Greeter.hello; hence the TypeError.

All object-oriented languages that are worth knowing (and many that aren’t) allow container objects to redirect access to their contained objects. But of the languages that I’ve used, Python is unique in allowing object attributes to redirect calls that attempt to access them. Objects that implement this capability are called descriptors1. As we’ll soon see, function objects, which Python uses to implement methods, are descriptors.

A full description of descriptors would be too long to fit here. Here are the most important points for this post:

  1. When the interpreter reads a class attribute, and the attribute value is an object that has a __get__ method, then the return value of that __get__ method is used as the attribute’s value.
  2. Methods are class attributes.
  3. Any callable object can serve as a method.

Here’s a good guide to descriptors. There’s also a recent post by Guido van Rossum, Python’s BDFL, that provides good background material on the feature.

To see where descriptors come into play, let’s look at the calling sequences for different versions of Greeter.hello. Here’s the rough sequence before Greeter.hello was decorated:

  • The interpreter searches for an attribute named hello on the Greeter instance, finding it in the Greeter class object.
  • The value of the Greeter.hello attribute (a function object) has a __get__ method, making it a descriptor, so that __get__ method is invoked. It’s passed a reference to the Greeter instance (obj) through which Greeter.hello was called.
  • The function object’s __get__ method creates and returns another callable object, which we’ll refer to as a (bound) method object. The method object references both the Greeter.hello function and obj.
  • The interpreter invokes the method object, passing it the arguments from the call to Greeter.hello (an empty list.) The method object then calls Greeter.hello, passing obj as the first argument, followed by the (empty) argument list.

When Greeter.hello is decorated with the DebugTracer class as shown above, a call to Greeter.hello runs more or less like this:

  • The interpreter searches for an attribute named hello on the Greeter instance, finding it in the Greeter class object.
  • The value of the Greeter.hello attribute is an instance of DebugTrace. This isn’t a function object, and it doesn’t have a __get__ method, but it does have a __call__ method. That __call__ method is invoked with the empty argument list.
  • DebugTrace.__call__ then calls Greeter.hello with the empty argument list.
  • Since Greeter.hello was looking for a single argument (self), rather than an empty argument list, a TypeError is raised.

To fix DebugTrace, I turned it into a descriptor class, adding a __get__ method that fills the same role as a function object’s __get__ method. However, this method binds the Greeter instance to the callable DebugTrace object.

import types
# ...
    def __get__(self, obj, ownerClass=None):
        # Return a wrapper that binds self as a method of obj (!)
        return types.MethodType(self, obj)

Compare the new calling sequence for Greeter.hello to the sequence prior to decoration:

  • The interpreter finds an attribute named hello in the Greeter class object.
  • The value of the Greeter.hello attribute is an instance of DebugTrace, which is now a descriptor. DebugTrace.__get__ is called, with obj (the Greeter instance) passed as one of the arguments.
  • DebugTrace.__get__ creates and returns a method object. The method object references both the DebugTrace instance and obj.
  • The interpreter invokes the method object, passing it the arguments from the call to Greeter.hello (an empty list.) The method object then calls DebugTrace.__call__. That in turn calls Greeter.hello, passing obj as the first argument, followed by the (empty) argument list.

It’s worth noting that DebugTrace.__get__ is only invoked when accessing a DebugTrace object through an object’s class dictionary. Hence its presence has no effect on functions that aren’t methods, such as greet.

You can see the full, working example here (click on the “show source” link to view) :

import types

class DebugTrace(object):
    def __init__(self, f):
        print("Tracing: {0}".format(f.__name__))
        self.f = f

    def __get__(self, obj, ownerClass=None):
        # Return a wrapper that binds self as a method of obj (!)
        return types.MethodType(self, obj)

    def __call__(self, *args, **kwargs):
        print("Calling: {0}".format(self.f.__name__))
        return self.f(*args, **kwargs)

class Greeter(object):
    instances = 0

    def __init__(self):
        Greeter.instances += 1
        self._inst = Greeter.instances

    @DebugTrace
    def hello(self):
        print("*** Greeter {0} says hello!".format(self._inst))

@DebugTrace
def greet():
    g = Greeter()
    g2 = Greeter()
    g.hello()
    g2.hello()

greet()

Executing the new version gives the desired output:

Tracing: hello
Tracing: greet
Calling greet
Calling hello
*** Greeter 1 says hello!
Calling hello
*** Greeter 2 says hello!
Credit Where Credit Is Due Dept: I’m not the first one to discover that a class-based decorator needs a __get__ method. For example, Ian Bicking wrote about a similar technique over a year and a half ago. However, Ian’s descriptor creates a new instance of the decorator class every time the method is invoked. I think the solution that I found—binding the original decorator instance to the method object—is different enough to be worth its own post.

For what it’s worth, I ran a simple performance test comparing Ian’s and my own class-based decorators, along with a function-based decorator. It showed no significant difference in performance among them. Apparently, the interpreter already optimizes these cases, which isn’t all that surprising.


1 I found the term “descriptor” to be somewhat confusing at first. A descriptor doesn’t really describe anything other than itself. To be fair, I don’t have any better suggestions. (“Redirector” ?) Naming is often one of the hardest challenges in software design, at least when it’s done right.

SyntaxHighlighter: Easier to load; faster to boot?

SyntaxHighlighter has a relatively high surface area, typically requiring two CSS files and at least two JavaScript files to be linked into a web page. Here’s a truncated example:

<html>
<head>
	<!-- Stylesheets for SyntaxHighlighter -->
<link type="text/css" rel="stylesheet" href="styles/shCore.css"/>
<link type="text/css" rel="stylesheet" href="styles/shThemeDefault.css"/>
...
</head>
<body>

<em>...Some kind of interesting page content usually goes here,
but we're not interested in that right now...</em>

	<!-- Load the SyntaxHighlighter Core and Brush scripts. A
               separate Brush script is required for each language. -->
<script type="text/javascript" src="scripts/shCore.js"></script>
<script type="text/javascript" src="scripts/shBrushJava.js"></script>
<script type="text/javascript" src="scripts/shBrushJScript.js"></script>
	<!-- Don't forget the XML brush if you're using html-script -->
<script type="text/javascript" src="scripts/shBrushXml.js"></script>

	<!-- Now that all the script files are loaded, begin highlighting. -->
<script type="text/javascript">
SyntaxHighlighter.config.stripBrs = true;
// ... additional configuration, as needed ...

SyntaxHighlighter.all();
</script>

</body>

Some users have wanted a simpler way to integrate SyntaxHighlighter into their sites. Last year, David Chambers wrote a script that uses the Prototype library to load the required CSS and brush files. He ran into a troubling quirk in SyntaxHighlighter: It needs any required brush files to be loaded before it starts highlighting. I responded by updating the OOWB fork of SyntaxHighlighter, so that brushes could be loaded asynchronously. But that’s as far as I’d gone towards automated loading, until now.

I recently looked into adding automated loading to SyntaxHighlighter itself, coming up with not one, but two automated solutions. I should really say one and a half, as one is still in the alpha stage and will likely stay there.

The “alpha-stage” approach comprises a new JavaScript file that loads SyntaxHighlighter and the necessary brush files through AJAX, by way of a pair of simple Java servlets. I’d hoped that this would lead to faster load times, by reducing the total number of downloads. And so it does—sometimes. For testing, I used Google App Engine as the servlet host (using the free quotas.) Performance was inconsistent, to say the least; this is probably due to the Google App Engine’s method of starting up and shutting down servlets. It seemed that only by making repeated hits on the servlets in rapid succession could I be sure of getting to a container that already had my servlets running, thus getting good load times.1 The servlets are still up there; if you’d like the Java source (to host them elsewhere), and/or the JavaScript for using them, just let me know.

I also implemented a pure JavaScript approach, which I hadn’t expected to yield better load times. Yet I’ve seen a number of cases where this is faster than the original, static way of loading SyntaxHighlighter. I’m releasing a new download so that you can try it out. I highly recommend that you test it thoroughly before putting it into real use.

To use this new method, the web page invokes SyntaxHighlighter.boot instead of SyntaxHighlighter.all. When boot is used as the entry point, SyntaxHighlighter determines which brushes are required by the web page, and issues HTTP requests for each brush file (as well as the appropriate CSS.) These requests use the age-old method of adding a tag for each brush, and a tag for each CSS file.

Using SyntaxHighlighter.boot, a web page only needs to add a couple of scripts, usually at the end of the . No CSS link elements are required.

<!--  We just need to load shCore.js near the end of the <body> element,
	then call SyntaxHighlighter.boot() with our configuration options. -->

<script type="text/javascript"
src="http://path/to/sh/directory/syntaxhighlighter/scripts/shCore.js"></script>

<script type="text/javascript">
	SyntaxHighlighter.boot(
		"http://path/to/syntaxhighlighter/root/syntaxhighlighter/",
		{theme : "Default"}, // Configuration settings
		{stripBrs : true}      // Default settings
	);
</script>

</body>

That’s it. Every page on your site can use the same set-up, regardless of which brush files it needs. (For another example, look at the HTML source for this page.)

When an HTML page includes external scripts—that is, elements with src attributes, the browser loads the script files synchronously, as it encounters them in the HTML. When the scripts are loaded dynamically, the browser can load them asynchronously; several scripts may then be loading simultaneously. This can be particularly helpful for pages that use multiple brushes. Here’s a Firebug sequence diagram of a web page with five brushes, loading in SyntaxHighlighter’s traditional way:

(Click on image for larger view)

Here’s the same page, modified to use the boot method. Notice that, unlike in the first picture, the shBrush...js files are loaded in parallel:

(Click on image for larger view)

Of course, this isn’t an entirely new discovery, but it came as a pleasant surprise nonetheless.

Availability

In addition to SyntaxHighlighter itself, I also modified Viper007Bond’s SyntaxHighlighter Evolved plugin for WordPress to use the new boot method. This simplified the code quite a bit, though it may have introduced bugs.

SyntaxHighlighter and SyntaxHighlighter Evolved are each available on the downloads page. Test before using. Note: If you’ve been using SyntaxHighlighter Evolved, you will probably need to re-set your settings if you install this plugin.

Implementation notes

In the traditional SyntaxHighlighter, all brushes must be loaded before SyntaxHighlighter.all is invoked. In this fork, “>brushes can load at any time after shCore.js has been loaded. This flexibility was key to the design of the boot method: If a brush is loaded after SyntaxHighlighter.all or SyntaxHighlighter.boot is invoked, SyntaxHighlighter looks through the page to see if there’s any input requesting the newly loaded brush.

The boot method needs to map each brush name to the script file that implements that brush. I implemented this by modifying the Perl script that builds and stages SyntaxHighlighter. The script now parses the *Brush.js files in the source tree, pulling out the names and aliases supported by each file, and writes them into an array in shCore.js. The downside of this is that testing via the boot method requires re-running the script, which builds and stages the files into a new directory, from where I can run tests. Even though the script only takes about a second, it’s crossed the boundary between having no build step in the development cycle, and having any build step in the development cycle.

When You Come To A Fork In The Road, Take It.

I had two motives for working on SyntaxHighlighter: One, to improve its display of code on my own blog and elsewhere. Two, as a training ground for learning JavaScript.

I was between jobs when I took on this project. As of this Monday, that will no longer be the case. That’s great news for me, but much as I’d like to keep the SyntaxHighlighter work moving forward, I don’t really know if or when I’ll have the time for it. On the other hand, I no longer feel like I need a private JavaScript training ground. So, I’ve pushed my repository to bitbucket.org as a fork of Alex’s repository. If he so chooses (and so far he hasn’t, which is fine), Alex is welcome to merge the fork into his branch. Perhaps more importantly, anyone else can now fork the code, and/or make contributions to this fork.

I do have some more goals in mind for SyntaxHighlighter. I’ll be writing about them in the not-too-distant future.


1Perhaps a faster host would lead to the faster load times I’d hoped for; but that would probably require payment, which makes this approach considerably less attractive.

Update of Forked SyntaxHighlighter

As promised, I’ve updated this site’s fork of the SyntaxHighlighter software by Alex Gorbatchev. I’ve merged in many of the changes that Alex included in his 2.1.364 release, but with the following differences:

  • In 2.1.364, the ruler functionality was removed. It’s still included here.
  • 2.1.364 changed the way that wrapped lines are presented. I’ve kept the old presentation, but modified the icon that’s used to signify wrapping.
  • Internally, 2.1.364 uses a separate HTML table to represent each line of source. I’ve maintained the older pattern of simply using a separate <div> to represent each line.

Other new bits in this release:

  • I’ve added a “Hide” button to the toolbar. Code blocks can now be expanded/collapsed at the user’s whim. (As before, an author can still choose to present a block in the hidden/collapsed state.)
  • When both a horizontal scrollbar and a gutter (the line number column) are displayed, the scrollbar no longer extends under the gutter. (I think this is an improvement; as always, feedback is welcome.) Update: I’ve backed out this change; it was causing problems with IE7.

The ZIP file is available through the Download Page.