1from typing import Dict 2from typing import Iterable 3from typing import List 4from typing import Optional 5from typing import Tuple 6from typing import Union 7 8from clikit.utils._compat import OrderedDict 9 10from ..exceptions import CannotAddArgumentException 11from ..exceptions import CannotAddOptionException 12from ..exceptions import NoSuchArgumentException 13from ..exceptions import NoSuchOptionException 14from .args_format import ArgsFormat 15from .argument import Argument 16from .option import Option 17from .command_name import CommandName 18from .command_option import CommandOption 19 20 21class ArgsFormatBuilder(object): 22 """ 23 A builder for ArgsFormat instances. 24 """ 25 26 def __init__(self, base_format=None): # type: (Optional[ArgsFormat]) -> None 27 self._base_format = base_format 28 self._command_names = [] 29 self._command_options = OrderedDict() 30 self._command_options_by_short_name = OrderedDict() 31 self._arguments = OrderedDict() 32 self._options = OrderedDict() 33 self._options_by_short_name = OrderedDict() 34 self._has_multi_valued_arg = False 35 self._hash_optional_arg = False 36 37 @property 38 def base_format(self): # type: () -> Optional[ArgsFormat] 39 return self._base_format 40 41 def set_command_names( 42 self, *command_names 43 ): # type: (Tuple[CommandName]) -> ArgsFormatBuilder 44 self._command_names = [] 45 46 self.add_command_names(*command_names) 47 48 return self 49 50 def add_command_names( 51 self, *command_names 52 ): # type: (Tuple[CommandName]) -> ArgsFormatBuilder 53 for command_name in command_names: 54 self.add_command_name(command_name) 55 56 return self 57 58 def add_command_name( 59 self, command_name 60 ): # type: (CommandName) -> ArgsFormatBuilder 61 self._command_names.append(command_name) 62 63 return self 64 65 def has_command_names(self, include_base=True): # type: (bool) -> bool 66 if self._command_names: 67 return True 68 69 if include_base and self._base_format: 70 return self._base_format.has_command_names() 71 72 return False 73 74 def get_command_names(self, include_base=True): # type: (bool) -> List[CommandName] 75 command_names = self._command_names 76 77 if include_base and self._base_format: 78 command_names = self._base_format.get_command_names() + command_names 79 80 return command_names 81 82 def set_command_options( 83 self, *command_options 84 ): # type: (Tuple[CommandOption]) -> ArgsFormatBuilder 85 self._command_options = {} 86 self._command_options_by_short_name = {} 87 88 self.add_command_options(*command_options) 89 90 def add_command_options( 91 self, *command_options 92 ): # type: (Tuple[CommandOption]) -> ArgsFormatBuilder 93 for command_option in command_options: 94 self.add_command_option(command_option) 95 96 def add_command_option( 97 self, command_option 98 ): # type: (CommandOption) -> ArgsFormatBuilder 99 long_name = command_option.long_name 100 short_name = command_option.short_name 101 long_aliases = command_option.long_aliases 102 short_aliases = command_option.short_aliases 103 104 if self.has_option(long_name) or self.has_command_option(long_name): 105 raise CannotAddOptionException.already_exists(long_name) 106 107 for long_alias in long_aliases: 108 if self.has_option(long_alias) or self.has_command_option(long_alias): 109 raise CannotAddOptionException.already_exists(long_alias) 110 111 if self.has_option(short_name) or self.has_command_option(short_name): 112 raise CannotAddOptionException.already_exists(short_name) 113 114 for short_alias in short_aliases: 115 if self.has_option(short_alias) or self.has_command_option(short_alias): 116 raise CannotAddOptionException.already_exists(short_alias) 117 118 self._command_options[long_name] = command_option 119 120 if short_name: 121 self._command_options_by_short_name[short_name] = command_option 122 123 for long_alias in long_aliases: 124 self._command_options[long_alias] = command_option 125 126 for short_alias in short_aliases: 127 self._command_options_by_short_name[short_alias] = command_option 128 129 return self 130 131 def has_command_option(self, name, include_base=True): # type: (str, bool) -> bool 132 if name in self._command_options or name in self._command_options_by_short_name: 133 return True 134 135 if include_base and self._base_format: 136 return self._base_format.has_command_option(name) 137 138 return False 139 140 def has_command_options(self, include_base=True): # type: (bool) -> bool 141 if self._command_options: 142 return True 143 144 if include_base and self._base_format: 145 return self._base_format.has_command_options() 146 147 return False 148 149 def get_command_option( 150 self, name, include_base=True 151 ): # type: (str, bool) -> CommandOption 152 if name in self._command_options: 153 return self._command_options[name] 154 155 if name in self._command_options_by_short_name: 156 return self._command_options_by_short_name[name] 157 158 if include_base and self._base_format: 159 return self._base_format.get_command_option(name) 160 161 raise NoSuchOptionException(name) 162 163 def get_command_options( 164 self, include_base=True 165 ): # type: (bool) -> Iterable[CommandOption] 166 command_options = list(self._command_options.values()) 167 168 if include_base and self._base_format: 169 command_options += self._base_format.get_command_options() 170 171 return command_options 172 173 def set_arguments( 174 self, *arguments 175 ): # type: (Iterable[Argument]) -> ArgsFormatBuilder 176 self._arguments = {} 177 self._has_multi_valued_arg = False 178 self._hash_optional_arg = False 179 180 self.add_arguments(*arguments) 181 182 return self 183 184 def add_arguments( 185 self, *arguments 186 ): # type: (Iterable[Argument]) -> ArgsFormatBuilder 187 for argument in arguments: 188 self.add_argument(argument) 189 190 return self 191 192 def add_argument(self, argument): # type: (Argument) -> ArgsFormatBuilder 193 name = argument.name 194 195 if self.has_argument(name): 196 raise CannotAddArgumentException.already_exists(name) 197 198 if self.has_multi_valued_argument(): 199 raise CannotAddArgumentException.cannot_add_after_multi_valued() 200 201 if argument.is_required() and self.has_optional_argument(): 202 raise CannotAddArgumentException.cannot_add_required_after_optional() 203 204 if argument.is_multi_valued(): 205 self._has_multi_valued_arg = True 206 207 if argument.is_optional(): 208 self._hash_optional_arg = True 209 210 self._arguments[name] = argument 211 212 return self 213 214 def has_argument( 215 self, name, include_base=True 216 ): # type: (Union[str, int], bool) -> bool 217 arguments = self.get_arguments(include_base) 218 219 if isinstance(name, int): 220 return name < len(arguments) 221 222 return name in arguments 223 224 def has_multi_valued_argument(self, include_base=True): # type: (bool) -> bool 225 if self._has_multi_valued_arg: 226 return True 227 228 if include_base and self._base_format: 229 return self._base_format.has_multi_valued_argument() 230 231 return False 232 233 def has_optional_argument(self, include_base=True): # type: (bool) -> bool 234 if self._hash_optional_arg: 235 return True 236 237 if include_base and self._base_format: 238 return self._base_format.has_optional_argument() 239 240 return False 241 242 def has_required_argument(self, include_base=True): # type: (bool) -> bool 243 if not self._hash_optional_arg and self._arguments: 244 return True 245 246 if include_base and self._base_format: 247 return self._base_format.has_required_argument() 248 249 return False 250 251 def has_arguments(self, include_base=True): # type: (bool) -> bool 252 if self._arguments: 253 return True 254 255 if include_base and self._base_format: 256 return self._base_format.has_arguments() 257 258 return False 259 260 def get_argument( 261 self, name, include_base=True 262 ): # type: (Union[str, int], bool) -> Argument 263 if isinstance(name, int): 264 arguments = list(self.get_arguments(include_base).values()) 265 266 if name >= len(arguments): 267 raise NoSuchArgumentException(name) 268 else: 269 arguments = self.get_arguments(include_base) 270 271 if name not in arguments: 272 raise NoSuchArgumentException(name) 273 274 return arguments[name] 275 276 def get_arguments(self, include_base=True): # type: (bool) -> Dict[str, Argument] 277 arguments = self._arguments.copy() 278 279 if include_base and self._base_format: 280 arguments.update(self._base_format.get_arguments()) 281 282 return arguments 283 284 def set_options(self, *options): # type: (Iterable[Option]) -> ArgsFormatBuilder 285 self._options = {} 286 self._options_by_short_name = {} 287 288 self.add_options(*options) 289 290 def add_options(self, *options): # type: (Iterable[Option]) -> ArgsFormatBuilder 291 for option in options: 292 self.add_option(option) 293 294 def add_option(self, option): # type: (Option) -> ArgsFormatBuilder 295 long_name = option.long_name 296 short_name = option.short_name 297 298 if self.has_option(long_name) or self.has_command_option(long_name): 299 raise CannotAddOptionException.already_exists(long_name) 300 301 if self.has_option(short_name) or self.has_command_option(short_name): 302 raise CannotAddOptionException.already_exists(short_name) 303 304 self._options[long_name] = option 305 306 if short_name: 307 self._options_by_short_name[short_name] = option 308 309 return self 310 311 def has_option(self, name, include_base=True): # type: (str, bool) -> bool 312 if name in self._options or name in self._options_by_short_name: 313 return True 314 315 if include_base and self._base_format: 316 return self._base_format.has_option(name) 317 318 return False 319 320 def has_options(self, include_base=True): # type: (bool) -> bool 321 if self._options: 322 return True 323 324 if include_base and self._base_format: 325 return self._base_format.has_options() 326 327 return False 328 329 def get_option(self, name, include_base=True): # type: (str, bool) -> Option 330 if name in self._options: 331 return self._options[name] 332 333 if name in self._options_by_short_name: 334 return self._options_by_short_name[name] 335 336 if include_base and self._base_format: 337 return self._base_format.get_option(name) 338 339 raise NoSuchOptionException(name) 340 341 def get_options(self, include_base=True): # type: (bool) -> Dict[str, Option] 342 options = self._options.copy() 343 344 if include_base and self._base_format: 345 options.update(self._base_format.get_options()) 346 347 return options 348 349 @property 350 def format(self): # type: () -> ArgsFormat 351 return ArgsFormat(self, self._base_format) 352