Search code examples
python-3.xreturn-valuegetattrdynamic-methodhasattr

How to return value with dynamic method calls through getattr()


I have a class that is called that runs a while loop command prompt, i am using dir() and getattr() to dynamically create a list of methods for a command shell. I want to return values, but return from a dynamically called method just exits to main while loop, why how can i fix this?

class myClass :
    def __init__(self) :
        self.commands = []
        for x in dir(self) :
                k = getattr( self, x )
                if hasattr(k, '__func__') :
                    self.commands.append(x)
            # strips off __init__ and __main__
            self.commands = self.commands[2:]

    def help(self, args=None) :
        for x in self.commands :

            ####
            #### The issue is here
            print('Command: {}'.format(x))
            f = getattr(self, x)('-h')
            desc = f()
            print('Description: {}'.format(desc))
        ...
        return SomeValue

    def cmd_b(self, args=None) :
        if args == '-h' :
            return 'Some description'
        ...
        return SomeValue

    def cmd_c(self, args=None) :
        ...
        return SomeValue

    def __main__(self) :
        while True :
            command = input(self.ENV['PS1'])
            command += ' '
            command = command.split()
            print(command)
            if len(command) > 1 :
                print(command)
                print(len(command))
                args = command[1:]
                command = command[0]
            else :
                command = command[0]
                args = None

            if command == 'exit'  :
                break

            if command not in dir(self) :
                print("Command `{}` not found".format(command))
                continue
            print(command)
            f = getattr( self, command )(args)
            x = f()

Solution

  • getattr and return values

    When you do getattr(self, attr) you get back the corresponding object and it's identical to calling the attribute directly. E.g:

    class A:
        def __init__(self):
            self.v = 42
        def func(self):
            return "Remember your towel"
    
    a = A()
    

    The following are equivalent

    value = a.v
    towel = a.func()
    
    value = getattr(a, 'v')
    towel = getattr(a, 'func')()
    
    f = getattr(a, 'func')
    towel = f()
    

    the variables value and towels are 42 and "Remember your towel" in both cases.

    This should respond to your questions.

    HOWEVER:

    The main problem in the code

    However the main problem in the code has nothing to do with the getattr.

    In the help method you refer to a f function that you never define, so that raises an exception.

    But the big problem is at the end of __main__:

    except:
        pass
    

    at the end. You should never ever do this: you are silencing errors, making it very hard to debug.

    If you want to make your code resilient to errors you should catch all the errors and report them somewhere, e.g. using log messages:

    import logging
    
    log = logging.getLogger()
    # set up the logger
    
    while:
        try:
            func()
        except Exception:
            log.exception('my nice message for the user')
    

    If you do somethign like this in your code, it would be much easier to catch errors like the undefined f function.

    Some suggestions:

    • In the help function, you loop over all the commands, help itself included, and print their help. The way you have implemented the help, this causes an infinite recursion. You should skip calling help from the help function.

    • all the commands return None or a string. When you will fix the code as indicated above, you will get a lot of errors from line 63: f()

    ps: If you are starting with python, give a look at PEP8, the official python style guidelines