commandline.py 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164
  1. import argparse
  2. import shlex
  3. import sys
  4. from copy import deepcopy
  5. from ..config import Configuration
  6. from ..setting import BooleanSetting, ChoiceSetting, ListSetting
  7. from .base import Source
  8. __all__ = ("CommandLineSource",)
  9. # pylint: disable=R0201
  10. class CommandLineSource(Source):
  11. # pylint: disable=R0913
  12. def __init__(
  13. self,
  14. arguments=None,
  15. options=None,
  16. version=None,
  17. parser_options=None,
  18. positional=None,
  19. ):
  20. super(CommandLineSource, self).__init__()
  21. if arguments is None:
  22. self.arguments = sys.argv[1:]
  23. elif isinstance(arguments, str):
  24. self.arguments = shlex.split(arguments)
  25. elif isinstance(arguments, (list, tuple)):
  26. self.arguments = arguments
  27. else:
  28. raise TypeError("arguments must be a string or list of strings")
  29. self.version = version
  30. self.options = options or {}
  31. self.parser_options = parser_options or {}
  32. self.positional = positional or ()
  33. def get_flags(self, setting):
  34. if setting.name in self.options:
  35. if "flags" in self.options[setting.name]:
  36. return self.options[setting.name]["flags"]
  37. flags = []
  38. flag = "--%s" % setting.name.lower().replace("_", "-")
  39. flags.append(flag)
  40. return flags
  41. def get_action(self, setting):
  42. if isinstance(setting, BooleanSetting):
  43. return "store_false" if setting.default else "store_true"
  44. elif isinstance(setting, ListSetting):
  45. return "append"
  46. else:
  47. return "store"
  48. # pylint: disable=W0613
  49. def get_default(self, setting):
  50. # Caveat: Returning something other than SUPPRESS probably won't
  51. # work the way you'd think.
  52. return argparse.SUPPRESS
  53. def get_type(self, setting):
  54. if isinstance(setting, (ListSetting, BooleanSetting)):
  55. return None
  56. elif isinstance(setting, ChoiceSetting):
  57. return setting.subtype.sanitize
  58. else:
  59. return setting.sanitize
  60. def get_dest(self, setting):
  61. return setting.name
  62. def get_choices(self, setting):
  63. if isinstance(setting, ChoiceSetting):
  64. return setting.choices
  65. else:
  66. return None
  67. def get_help(self, setting):
  68. if setting.name in self.options:
  69. if "help" in self.options[setting.name]:
  70. return self.options[setting.name]["help"]
  71. return None
  72. def get_metavar(self, setting):
  73. if setting.name in self.options:
  74. if "metavar" in self.options[setting.name]:
  75. return self.options[setting.name]["metavar"]
  76. return None
  77. def build_argument(self, setting):
  78. flags = self.get_flags(setting)
  79. action = self.get_action(setting)
  80. default = self.get_default(setting)
  81. argtype = self.get_type(setting)
  82. dest = self.get_dest(setting)
  83. choices = self.get_choices(setting)
  84. arghelp = self.get_help(setting)
  85. metavar = self.get_metavar(setting)
  86. argument_kwargs = {
  87. "action": action,
  88. "default": default,
  89. "dest": dest,
  90. "help": arghelp,
  91. }
  92. if argtype:
  93. argument_kwargs["type"] = argtype
  94. if choices:
  95. argument_kwargs["choices"] = choices
  96. if metavar:
  97. argument_kwargs["metavar"] = metavar
  98. return flags, argument_kwargs
  99. def build_parser(self, settings, manager):
  100. parser_options = deepcopy(self.parser_options)
  101. if not parser_options.get("prog") and manager:
  102. parser_options["prog"] = manager.name
  103. parser = argparse.ArgumentParser(**parser_options)
  104. add_version = self.version is not None
  105. for setting in settings:
  106. flags, argument_kwargs = self.build_argument(setting)
  107. parser.add_argument(*flags, **argument_kwargs)
  108. if add_version and setting.name == "version":
  109. # Don't want to conflict with the desired setting
  110. add_version = False
  111. if add_version:
  112. parser.add_argument(
  113. "--version", action="version", version="%(prog)s " + self.version
  114. )
  115. if self.positional:
  116. for name, options in self.positional:
  117. parser.add_argument(name, **options)
  118. return parser
  119. def get_config(self, settings, manager=None, parent=None):
  120. parser = self.build_parser(settings, manager)
  121. parsed = parser.parse_args(self.arguments)
  122. for setting in settings:
  123. if hasattr(parsed, setting.name):
  124. setting.value = getattr(parsed, setting.name)
  125. if self.positional and manager:
  126. arguments = {}
  127. for name, _ in self.positional:
  128. if hasattr(parsed, name):
  129. arguments[name] = getattr(parsed, name)
  130. setattr(manager, "arguments", arguments)
  131. return Configuration(settings=settings, parent=parent)