Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/face/errors.py: 69%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

70 statements  

1from boltons.iterutils import unique 

2 

3import face.utils 

4 

5class FaceException(Exception): 

6 """The basest base exception Face has. Rarely directly instantiated 

7 if ever, but useful for catching. 

8 """ 

9 pass 

10 

11 

12class ArgumentParseError(FaceException): 

13 """A base exception used for all errors raised during argument 

14 parsing. 

15 

16 Many subtypes have a ".from_parse()" classmethod that creates an 

17 exception message from the values available during the parse 

18 process. 

19 """ 

20 pass 

21 

22 

23class ArgumentArityError(ArgumentParseError): 

24 """Raised when too many or too few positional arguments are passed to 

25 the command. See PosArgSpec for more info. 

26 """ 

27 pass 

28 

29 

30class InvalidSubcommand(ArgumentParseError): 

31 """ 

32 Raised when an unrecognized subcommand is passed. 

33 """ 

34 @classmethod 

35 def from_parse(cls, prs, subcmd_name): 

36 # TODO: add edit distance calculation 

37 valid_subcmds = unique([path[:1][0] for path in prs.subprs_map.keys()]) 

38 msg = ('unknown subcommand "%s", choose from: %s' 

39 % (subcmd_name, ', '.join(valid_subcmds))) 

40 return cls(msg) 

41 

42 

43class UnknownFlag(ArgumentParseError): 

44 """ 

45 Raised when an unrecognized flag is passed. 

46 """ 

47 @classmethod 

48 def from_parse(cls, cmd_flag_map, flag_name): 

49 # TODO: add edit distance calculation 

50 valid_flags = unique([face.utils.format_flag_label(flag) for flag in 

51 cmd_flag_map.values() if not flag.display.hidden]) 

52 msg = f"unknown flag \"{flag_name}\", choose from: {', '.join(valid_flags)}" 

53 return cls(msg) 

54 

55 

56class InvalidFlagArgument(ArgumentParseError): 

57 """Raised when the argument passed to a flag (the value directly 

58 after it in argv) fails to parse. Tries to automatically detect 

59 when an argument is missing. 

60 """ 

61 @classmethod 

62 def from_parse(cls, cmd_flag_map, flag, arg, exc=None): 

63 if arg is None: 

64 return cls(f'expected argument for flag {flag.name}') 

65 

66 val_parser = flag.parse_as 

67 vp_label = getattr(val_parser, 'display_name', face.utils.FRIENDLY_TYPE_NAMES.get(val_parser)) 

68 if vp_label is None: 

69 vp_label = repr(val_parser) 

70 tmpl = 'flag %s converter (%r) failed to parse value: %r' 

71 else: 

72 tmpl = 'flag %s expected a valid %s value, not %r' 

73 msg = tmpl % (flag.name, vp_label, arg) 

74 

75 if exc: 

76 # TODO: put this behind a verbose flag? 

77 msg += f' (got error: {exc!r})' 

78 if arg.startswith('-'): 

79 msg += '. (Did you forget to pass an argument?)' 

80 

81 return cls(msg) 

82 

83 

84class InvalidPositionalArgument(ArgumentParseError): 

85 """Raised when one of the positional arguments does not 

86 parse/validate as specified. See PosArgSpec for more info. 

87 """ 

88 @classmethod 

89 def from_parse(cls, posargspec, arg, exc): 

90 prep, type_desc = face.utils.get_type_desc(posargspec.parse_as) 

91 return cls('positional argument failed to parse %s' 

92 ' %s: %r (got error: %r)' % (prep, type_desc, arg, exc)) 

93 

94 

95class MissingRequiredFlags(ArgumentParseError): 

96 """ 

97 Raised when a required flag is not passed. See Flag for more info. 

98 """ 

99 @classmethod 

100 def from_parse(cls, cmd_flag_map, parsed_flag_map, missing_flag_names): 

101 flag_names = set(missing_flag_names) 

102 labels = [] 

103 for flag_name in flag_names: 

104 flag = cmd_flag_map[flag_name] 

105 labels.append(face.utils.format_flag_label(flag)) 

106 msg = f"missing required arguments for flags: {', '.join(sorted(labels))}" 

107 return cls(msg) 

108 

109 

110class DuplicateFlag(ArgumentParseError): 

111 """Raised when a flag is passed multiple times, and the flag's 

112 "multi" setting is set to 'error'. 

113 """ 

114 @classmethod 

115 def from_parse(cls, flag, arg_val_list): 

116 avl_text = ', '.join([repr(v) for v in arg_val_list]) 

117 if callable(flag.parse_as): 

118 msg = f'more than one value was passed for flag "{flag.name}": {avl_text}' 

119 else: 

120 msg = f'flag "{flag.name}" was used multiple times, but can be used only once' 

121 return cls(msg) 

122 

123 

124## Non-parse related exceptions (used primarily in command.py instead of parser.py) 

125 

126class CommandLineError(FaceException, SystemExit): 

127 """A :exc:`~face.FaceException` and :exc:`SystemExit` subtype that 

128 enables safely catching runtime errors that would otherwise cause 

129 the process to exit. 

130 

131 If instances of this exception are left uncaught, they will exit 

132 the process. 

133 

134 If raised from a :meth:`~face.Command.run()` call and 

135 ``print_error`` is True, face will print the error before 

136 reraising. See :meth:`face.Command.run()` for more details. 

137 """ 

138 def __init__(self, msg, code=1): 

139 SystemExit.__init__(self, msg) 

140 self.code = code 

141 

142 

143class UsageError(CommandLineError): 

144 """Application developers should raise this :exc:`CommandLineError` 

145 subtype to indicate to users that the application user has used 

146 the command incorrectly. 

147 

148 Instead of printing an ugly stack trace, Face will print a 

149 readable error message of your choosing, then exit with a nonzero 

150 exit code. 

151 """ 

152 

153 def format_message(self): 

154 msg = self.args[0] 

155 lines = msg.splitlines() 

156 msg = '\n '.join(lines) 

157 return 'error: ' + msg