Source code for aiida.cmdline.baseclass

# -*- coding: utf-8 -*-
###########################################################################
# Copyright (c), The AiiDA team. All rights reserved.                     #
# This file is part of the AiiDA code.                                    #
#                                                                         #
# The code is hosted on GitHub at https://github.com/aiidateam/aiida_core #
# For further information on the license, see the LICENSE.txt file        #
# For further information please visit http://www.aiida.net               #
###########################################################################



[docs]class VerdiCommand(object): """ This command has no documentation yet. """ class __metaclass__(type): """ Some python black magic to set correctly the logger also in subclasses. """ def __new__(cls, name, bases, attrs): newcls = type.__new__(cls, name, bases, attrs) # If the '_abstract' attribute is not explicitly defined in the # given class, set it to False. if '_abstract' not in attrs: newcls._abstract = False return newcls # This is an abstract class _abstract = True # To be defined if you want that the name is not generated by the class # name, but from a given string _custom_command_name = None
[docs] def get_full_command_name(self, with_exec_name=True): """ Return the current command name. Also tries to get the subcommand name. :param with_exec_name: if True, return the full string, including the executable name ('verdi'). If False, omit it. """ from aiida.cmdline import execname subcommand_str = "" if with_exec_name: exec_name_part = "{} ".format(execname) else: exec_name_part = "" return "{}{}".format(exec_name_part, self.get_command_name())
@classmethod
[docs] def get_command_name(cls): """ Return the name of the verdi command associated to this class. By default, the lower-case version of the class name. """ if cls._custom_command_name is None: return cls.__name__.lower() else: return cls._custom_command_name
[docs] def run(self, *args): """ Method executed when the command is called from the command line. """ import sys print >> sys.stderr, "This command has not been implemented yet"
[docs] def complete(self, subargs_idx, subargs): """ Method called when the user asks for the bash completion. Print a list of valid keywords. Returning without printing will use standard bash completion. :param subargs_idx: the index of the subargs where the TAB key was pressed\ (0 is the first element of subargs) :param subargs: a list of subarguments to this command """ return
class VerdiCommandRouter(VerdiCommand): _abstract = True # Empty valid subcommands to start with; # These should be a dictionary with 'key' the name to type on the # command line, and value a VerdiCommand class to call when that subcommand # is invoked. routed_subcommands = {} def no_subcommand(self, *args): import sys if self.routed_subcommands: print >> sys.stderr, ("You have to pass a valid subcommand to " "{}.\nValid subcommands are:".format( self.get_full_command_name())) print >> sys.stderr, "\n".join(" {}".format(sc) for sc in self.routed_subcommands) else: print >> sys.stderr, ("There are no valid subcommand to " "{}.".format(self.get_full_command_name())) sys.exit(1) def invalid_subcommand(self, *args): import sys if self.routed_subcommands: print >> sys.stderr, ("You passed an invalid subcommand to '{}'.\n" "Valid subcommands are:".format( self.get_full_command_name())) print >> sys.stderr, "\n".join(" {}".format(sc) for sc in self.routed_subcommands) else: print >> sys.stderr, ("There are no valid subcommand to " "{}.".format(self.get_full_command_name())) sys.exit(1) def run(self, *args): try: the_class = self.routed_subcommands[args[0]] the_class._custom_command_name = "{} {}".format( self.get_full_command_name(with_exec_name=False), args[0]) function_to_call = the_class().run except IndexError: function_to_call = self.no_subcommand except KeyError: function_to_call = self.invalid_subcommand function_to_call(*args[1:]) def complete(self, subargs_idx, subargs): if subargs_idx == 0: print "\n".join(self.routed_subcommands.keys()) elif subargs_idx >= 1: try: first_subarg = subargs[0] except IndexError: first_subarg = '' try: complete_function = self.routed_subcommands[ first_subarg]().complete except KeyError: print "" return complete_function(subargs_idx - 1, subargs[1:])
[docs]class VerdiCommandWithSubcommands(VerdiCommand): """ Used for commands with subcommands. Just define, in the __init__, the self.valid_subcommands dictionary, in the format:: self.valid_subcommands = { 'uploadfamily': (self.uploadfamily, self.complete_auto), 'listfamilies': (self.listfamilies, self.complete_none), } where the key is the subcommand name to give on the command line, and the value is a tuple of length 2, the first is the function to call on execution, the second is the function to call on complete. This class already defined the complete_auto and complete_none commands, that respectively call the default bash completion for filenames/folders, or do not give any completion suggestion. Other functions can of course be defined. .. todo:: Improve the docstrings for commands with subcommands. """ _abstract = True valid_subcommands = {} def run(self, *args): try: function_to_call = self.valid_subcommands[args[0]][0] except IndexError: function_to_call = self.no_subcommand except KeyError: function_to_call = self.invalid_subcommand function_to_call(*args[1:]) def complete(self, subargs_idx, subargs): if subargs_idx == 0: print "\n".join(self.valid_subcommands.keys()) elif subargs_idx >= 1: try: first_subarg = subargs[0] except IndexError: first_subarg = '' try: complete_function = self.valid_subcommands[first_subarg][1] except KeyError: print "" return complete_data = complete_function(subargs_idx - 1, subargs[1:]) if complete_data is not None: print complete_data def complete_none(self, subargs_idx, subargs): return "" def complete_auto(self, subargs_idx, subargs): return None def no_subcommand(self, *args): import sys if self.valid_subcommands: print >> sys.stderr, ("You have to pass a valid subcommand to " "'{}'.\nValid subcommands are:".format( self.get_full_command_name())) print >> sys.stderr, "\n".join(" {}".format(sc) for sc in self.valid_subcommands) else: print >> sys.stderr, ("There are no valid subcommands to " "'{}'.".format(self.get_full_command_name())) sys.exit(1) def invalid_subcommand(self, *args): import sys if self.valid_subcommands: print >> sys.stderr, ("You passed an invalid subcommand to '{}'.\n" "Valid subcommands are:".format( self.get_full_command_name())) print >> sys.stderr, "\n".join(" {}".format(sc) for sc in self.valid_subcommands) else: print >> sys.stderr, ("There are no valid subcommands to " "'{}'.".format(self.get_full_command_name())) sys.exit(1)
[docs] def get_full_command_name(self, *args, **kwargs): """ Return the current command name. Also tries to get the subcommand name. Also tries to see if the caller function was one specific submethod. :param with_exec_name: if True, return the full string, including the executable name ('verdi'). If False, omit it. """ import inspect from aiida.cmdline import execname subcommand_str = "" try: # [0]: this function; # [1]: function that directly called this function # So I go in order from the function that called to the above # function, etc. until I find it, if I can found = False for caller_function in inspect.stack()[1:]: if found == True: break for k, v in self.valid_subcommands.iteritems(): if v[0].__name__ == caller_function[3]: subcommand_str = " {}".format(k) found = True break except (KeyError, AttributeError, IndexError): # Some of this info could not be retrived, do not set # the subcommand name pass return "{}{}".format( super(VerdiCommandWithSubcommands, self).get_full_command_name( *args, **kwargs), subcommand_str)