6

I have a class named Server which can be started and stopped. Certain methods should not be called unless the Server is started, in which case a NotConnectedException should be raised. Is there a way to call a method before every method in a class and determine if class variable _started is set to True?

I tried using a decorator, but the decorator function does not have access to the class variable. I was trying to do something like this:

class Server(object):
    _started = False
    def started(self):
        if(self._started == False):
            raise NotConnectedException

    @started
    def doServerAction(self):
       ...
BlueVoid
  • 929
  • 1
  • 9
  • 26
  • did you try putting the decorator directly in the class? I haven't worked enough on decorators to know how well this would work, but it's where I would start looking – inspectorG4dget Jun 05 '12 at 20:53
  • as this [post](http://stackoverflow.com/questions/2366713/can-a-python-decorator-of-an-instance-method-access-the-class) said, if it's python2.6 or later, there is a way for your decorator to access the class variable – xvatar Jun 05 '12 at 20:54
  • @inspectorG4dget Yes, the decorator is in the class. – BlueVoid Jun 05 '12 at 21:18
  • @xvatar It looks like that might be close, but I'm still not sure how I would access the class variable in the method decorator. – BlueVoid Jun 05 '12 at 21:27

1 Answers1

9

Remember what decorators are:

@decorate
def foo(...):
    ...

is exactly equivalent to:

def foo(...):
    ...
foo = decorate(foo)

The decorator is called on the function, so calling the first parameter self makes no sense. Also, the decorator is called on the function when it is defined, and whatever it returns is used in place of the function. So even if your started decorator didn't throw an AttributeError by trying to access the _started attribute of a function, it would then return None, making all your methods set to None, and thus not even be callable.

What you want is something like this:

import functools

def started(func):
    @functools.wraps(func)
    def wrapper(self, *args, **kwargs):
        if not self._started:
            raise ...
        else:
            return func(self, *args, **kwargs)
    return wrapper

Almost all decorators are of this form; they take a function, create a wrapper that does something "around" the received function, and then return the wrapper. The use of functools.wraps here is a convenience if you ever end up working with this code in an interactive interpreter session; it automatically updates the wrapper function with the name and docstring of the original function, which makes the decorated functions "look like" the original function a bit more.

It's irrelevant whether this is defined inside the class or not.

Ben
  • 68,572
  • 20
  • 126
  • 174