0

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()
Aesycos
  • 3
  • 1
  • 5
  • Can you please fix the indentation? The ``__main__`` function is off – Francesco Montesano Sep 13 '17 at 08:44
  • no its part of the class (myClass.__main__()), i wasn't trying to post the entire code cause i already figured what i posted was probably frowned upon. @FrancescoMontesano – Aesycos Sep 13 '17 at 08:51
  • What [Francesco](https://stackoverflow.com/users/1860757/francesco-montesano) said. Also seeing `def myClass :` `SyntaxError: invalid syntax`. You probably want `def myClass(object):`. – jq170727 Sep 13 '17 at 08:52
  • oops yeah was supposed to be `class myClass` should i just post the entire code its not much longer really, i thought i added enough for it to make sense @jq170727 – Aesycos Sep 13 '17 at 08:53
  • Yes, do correct your code. On StackOverflow always do your best to post an [MCVE - Minimal, Complete, and Verifiable example](https://stackoverflow.com/help/mcve/) – jq170727 Sep 13 '17 at 09:00
  • @Aesycos: now the indentation is ok. Does it just exit the while loop or you get an error. May I ask why you are writing the whole thing by yourself instead of using libraries like [the builtin ``cmd`` module](https://docs.python.org/3/library/cmd.html#module-cmd) – Francesco Montesano Sep 13 '17 at 09:08
  • So after I create an instance of `myClass` I call `myClass.__main__()` which starts the while loop and displays a command prompt, from there I type `help` which calls `cmd_b` and sends it the args value `'-h'` after cmd_b processes '-h' and returns `desc` is not set with the value `"Some description"` like expected it just returns to `myClass._main_()` at a prompt again, nor does it call the other functions with '-h' args - I guess I'm just doing it for practice and to learn. No reason other than I wanted to implement a command interpreter into some projects I'm working on. – Aesycos Sep 13 '17 at 20:32
  • @FrancescoMontesano I honestly didn't know about the built-in cmd module. But I'd still like to know if a function is called utilizing getattr can that function return a value to the calling method? Here is the complete code: [source](https://paste.pound-python.org/show/e1MhYGqw4ohqONYy8HAf/) – Aesycos Sep 13 '17 at 20:49
  • can you replace the code with the actual one?. It is much more useful that the mockup you have now and it is not much longer. Also the real code shows the actual problem. – Francesco Montesano Sep 14 '17 at 07:37

1 Answers1

0

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

Francesco Montesano
  • 8,485
  • 2
  • 40
  • 64
  • okay so when i call `getattr(self, command)` it is actually being called there. if i prefix it with `f =` then f contains the return value, which is what i couldn't find and why i was calling an undefined f(), because in my prticular case a `str` isn't callable nor a None value, i removed the try, except and added an `if command == 'help' : continue` thank you very very much for your help – Aesycos Sep 15 '17 at 07:27
  • you're welcome. I suggest you to keep the ``try-except`` block but to log the errors. This way your promtp will live even if there are errors (either bugs, or wrongly called commands) but you won't loose the information about the errors – Francesco Montesano Sep 15 '17 at 07:51