giovannipizzi / aiida-node-shell

Proof of concept of a way to easily browse AiiDA nodes through an interactive, customisable shell with tab completion
MIT License
6 stars 1 forks source link

Add custom commands only for specific data classes? #9

Open giovannipizzi opened 4 years ago

giovannipizzi commented 4 years ago

E.g., one could have a creator command that only works for data nodes and loads the creator, output [LABEL]/input [LABEL] commands that only work for processes, or status for processes etc.

A possible implementation follows. It works, but I'm not sure if this is going to be less or more confusing. The other options I see is to define them always but print a message if they don't make sense (a bit like what the @needs_node currently does)

Opionions welcome! @sphuber @zhubonan

#!/usr/bin/env python
import cmd2

class DisappearingCommands(cmd2.Cmd):
    """Example cmd2 application where commands exist depending on the value of an internal variable.

    This can be used e.g. to decide whether to show certain commands if a node is Data or Calculation, etc."""

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.do_set(["0"])

    def do_set(self, args):
        """This command does nothing and produces no output."""
        self.current = int(args[0])

        self._update_disappearing_commands()

    def _update_disappearing_commands(self):
        DISAPPEARING_CMD_PREFIX = 'disappearing_do_'
        DISAPPEARING_SHOW_PREFIX = 'show_'

        for member in dir(self):
            if not member.startswith(DISAPPEARING_CMD_PREFIX):
                continue

            command_name = member[len(DISAPPEARING_CMD_PREFIX):]
            command_function = getattr(self, member)
            try:
                check_function = getattr(self, f'{DISAPPEARING_SHOW_PREFIX}{command_name}')
            except AttributeError:
                # Both disappearing_do_CMD and show_CMD must be defined
                continue
            if check_function():
                setattr(self, f'do_{command_name}', command_function)
            else:
                try:
                    delattr(self, f'do_{command_name}')
                except AttributeError:
                    pass

    def disappearing_do_even(self, args):
        print(f"even, current: {self.current}")

    def show_even(self):
        return not self.current % 2

    def disappearing_do_odd(self, args):
        print(f"odd, current: {self.current}")

    def show_odd(self):
        return self.current % 2

    @property
    def prompt(self):
        """Define a custom prompt string."""
        return '(current={})$ '.format(self.current)

if __name__ == '__main__':
    import sys
    app = DisappearingCommands()
    sys.exit(app.cmdloop())

(execute it and call set <INT>; the even and odd commands are defined only if the number you set is even or odd, respectively)

zhubonan commented 4 years ago

Looks a bit complication to me. I think just print error is fine, also serves as a reminder of what is wrong, which is not the case if the command just doesn't exist.

As another thought, I think it may worth adding a command like follow_createor, follow_called (this will have multiple branches though) as a way to jump following the links.

Quite often I jump around using in -l 0, out -l 0, might be useful to have some shortcuts.