views:

88

answers:

3

I have a model class with getter and setter methods, and the occasional static methods. I would like to enforce the usage of unicode strings as arguments for specific methods and using decorators was the first idea I had. Now I have something like this:

import types

class require_unicode(object):

    def __init__(self, function):
        self.f = function

    def __call__(self, string):
        if not isinstance(string, types.UnicodeType):
            raise ValueError('String is not unicode')
        return self.f(string)

class Foo(object):

    something = 'bar'

    @staticmethod
    @require_unicode
    def do_another(self, string):
        return ' '.join(['baz', string])

    @require_unicode
    def set_something(self, string):
        self.something = string

foo = Foo()
foo.set_something('ValueError is raised')
foo.set_something(u'argument count error')
foo.do_another('ValueError is raised')
foo.do_another(u'argument count error')

In the above code the method call inside decorator's __call__ fails due to wrong argument count (because the 'foo' object ref is missing?). Before doing something stupid I wanted to ask you guys. How this should be done?

+1  A: 

I think, your problem is with the @staticmethod decorator, not with your require_unicode decorator. Staticmethods, unlike classmethods don't receive the reference to the class as the first argument, so your argument signature is wrong.

You must either change do_another to be a @classmethod, or remove self from the arguments.

EDIT: and, mind you, - @classmethod-decorated methods receive the class as the first argument, while instance methods receive the reference to the instance of the class (self). So it is a good idea to name the first argument to a classmethod "cls" or something, not "self" so it doesn't confuse anyone.

shylent
A: 

I would think that this was unpythonic - you should never check the type of your arguments, but instead check that they have the necessary methods and attributes. The simplest way to do this is by assuming they are there and getting an exception otherwise, but I guess you could do getattr too. Just don't check the type of things.

Teddy
I know it's unpythonic, but preventing encoded strings here would save me from a lot of trouble later on when saving the given data to database.
eclaird
@eclaird: Once you absolutely positively *require* unicode, just call `unicode()` on whatever you've got and call it good.
Teddy
A: 

Another option is to use assertions. It depends on whether passing a non-unicode type into your methods should be considered a programming error that should be evident during development.

import types
class Foo:
    def set_something(self, string):
        assert isinstance(string, types.UnicodeType), 'String is not unicode'
        self.something = string

This will raise an AssertionError exception whenever string is not of type unicode, but only when the Python interpretter is run in "deubg" mode. If you run Python with the -O option, the assert is efficiently ignored by the interpretter.

mhawke
Absolutely, I thought of this too but figured decorators would be more clean way to do it.
eclaird
Which actually looks cleaner to you then? IMO assert is a lot more obvious as the checks are right there at the start of the method, not hidden away in a class that might be imported from elsewhere. You also get a the performance benefit of having assert effectively become a NOP when running python with `-O`.
mhawke