docopt_command.py 1.8 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162
  1. from inspect import getdoc
  2. from docopt import docopt
  3. from docopt import DocoptExit
  4. def docopt_full_help(docstring, *args, **kwargs):
  5. try:
  6. return docopt(docstring, *args, **kwargs)
  7. except DocoptExit:
  8. raise SystemExit(docstring)
  9. class DocoptDispatcher:
  10. def __init__(self, command_class, options):
  11. self.command_class = command_class
  12. self.options = options
  13. @classmethod
  14. def get_command_and_options(cls, doc_entity, argv, options):
  15. command_help = getdoc(doc_entity)
  16. opt = docopt_full_help(command_help, argv, **options)
  17. command = opt['COMMAND']
  18. return command_help, opt, command
  19. def parse(self, argv):
  20. command_help, options, command = DocoptDispatcher.get_command_and_options(
  21. self.command_class, argv, self.options)
  22. if command is None:
  23. raise SystemExit(command_help)
  24. handler = get_handler(self.command_class, command)
  25. docstring = getdoc(handler)
  26. if docstring is None:
  27. raise NoSuchCommand(command, self)
  28. command_options = docopt_full_help(docstring, options['ARGS'], options_first=True)
  29. return options, handler, command_options
  30. def get_handler(command_class, command):
  31. command = command.replace('-', '_')
  32. # we certainly want to have "exec" command, since that's what docker client has
  33. # but in python exec is a keyword
  34. if command == "exec":
  35. command = "exec_command"
  36. if not hasattr(command_class, command):
  37. raise NoSuchCommand(command, command_class)
  38. return getattr(command_class, command)
  39. class NoSuchCommand(Exception):
  40. def __init__(self, command, supercommand):
  41. super().__init__("No such command: %s" % command)
  42. self.command = command
  43. self.supercommand = supercommand