Sitruc
Sitruc

Reputation: 109

Python Decorator Inheritance issue

class Foo():
    help_message = ""

    def Help_Decorator(func):
        def wrapper(data, context, caller):
            try:
                if data[0] == "help":
                    return(help_message) #<--- cannot access this locally
                else:
                    return func(data,context,caller)
            except:
                return func(data,context,caller)
    return wrapper

class Bar(Foo):
    help_message = "A real help message!"
    @foo.Help_Decorator
    def Run(data,context,caller):
        pass

How can I access Bar's help_message from within my Help_Decorator without passing it as a parameter in Run?

Upvotes: 0

Views: 72

Answers (2)

Samwise
Samwise

Reputation: 71562

Since Run is an instance method, data is a Bar instance. Normally this would be called self by convention, but giving it a different name doesn't change the semantics of the function call.

You should therefore be able to access data.help_message in your wrapper function. (I would also expect that data[0] gives you a TypeError unless Bar has implemented __getitem__.)

If your intent is to call Run on the class instead of an instance, it shouldn't be defined as an instance method. Make it a class method:

class Bar(): 
    help_message = "A real help message!" 

    @classmethod
    @foo.Help_Decorator
    def Run(cls, data, context, caller): 
        pass

and in your wrapper now you can do:

    def Help_Decorator(func):
        def wrapper(cls, data, context, caller):
            try:
                if data[0] == "help":
                    return(cls.help_message)
                else:
                    return func(cls, data, context, caller)
            except:
                return func(cls, data, context, caller)
    return wrapper

Note that there is no need for Help_Decorator to be a method of either Foo or Bar.

All together:

class Foo():
    help_message = ""

    def Help_Decorator(func):
        def wrapper(cls, data, context, caller):
            try:
                if data[0] == "help":
                    return(cls.help_message)
                else:
                    return func(cls, data, context, caller)
            except:
                return func(cls, data, context, caller)
        return wrapper

class Bar(Foo):
    help_message = "A real help message!"

    @classmethod
    @Foo.Help_Decorator
    def Run(cls, data, context, caller):
        return "derp"

print(Bar.Run(["help"], 1, 0))

prints:

A real help message!

Ripping Foo out of the code completely produces the same exact result; there is no need for any kind of inheritance relation between the place where Help_Decorator is implemented and the class whose methods it decorates:

def Help_Decorator(func):
    def wrapper(cls, data, context, caller):
        try:
            if data[0] == "help":
                return(cls.help_message)
            else:
                return func(cls, data, context, caller)
        except:
            return func(cls, data, context, caller)
    return wrapper

class Bar:
    help_message = "A real help message!"

    @classmethod
    @Help_Decorator
    def Run(cls, data, context, caller):
        return "derp"

print(Bar.Run(["help"], 1, 0))

Upvotes: 2

Lachlan
Lachlan

Reputation: 175

The first parameter passed to the wrapper is an instance of the Run class, you should add self as the first parameter. Then you can refer to self.help_message and it will return the help_message defined in the Run class.

(Edit) If you would like to use the decorator as a classmethod then just decorate it as such. Your example would become.

class foo(): 
    help_message = "bad" 
 
    @classmethod     
    def Help_Decorator(cls, func): 
        @classmethod 
        def wrapper(cls, data, context, caller): 
            print(data, context, caller) 
            try: 
                if data[0] == "help": 
                    print('helping', cls) 
                    return(cls.help_message) #<--- cannot access this locally 
                else: 
                    return func(data,context,caller) 
            except: 
                return func(data,context,caller) 
        return wrapper 
 
class Bar(): 
    help_message = "A real help message!" 
 
    @foo.Help_Decorator 
    @classmethod 
    def Run(cls, data,context,caller): 
        pass

Upvotes: 0

Related Questions