Coverage for /opt/cloud/slavespace/usr1/096471637100f3de0fcfc01072822a80/ut/src/asys/cmdline/cmd_parser.py: 99%

160 statements  

« prev     ^ index     » next       coverage.py v7.14.1, created at 2026-08-21 15:37 +0800

1#!/usr/bin/env python3 

2# -*- coding: utf-8 -*- 

3# ---------------------------------------------------------------------------- 

4# Copyright (c) 2025 Huawei Technologies Co., Ltd. 

5# 

6# Licensed under the Apache License, Version 2.0 (the "License"); 

7# you may not use this file except in compliance with the License. 

8# You may obtain a copy of the License at 

9# 

10# http://www.apache.org/licenses/LICENSE-2.0 

11# 

12# Unless required by applicable law or agreed to in writing, software 

13# distributed under the License is distributed on an "AS IS" BASIS, 

14# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 

15# See the License for the specific language governing permissions and 

16# limitations under the License. 

17# ---------------------------------------------------------------------------- 

18 

19import argparse 

20import sys 

21import textwrap 

22import enum 

23 

24from params import ParamDict 

25from common.const import RetCode, consts 

26from cmdline.arg_checker import ArgChecker 

27 

28KEY_NAME = "name" 

29KEY_TYPE = "type" 

30KEY_HELP = "help" 

31KEY_CHECKER = "checker" 

32KEY_REQUIRED = "required" 

33KEY_ARGS = "args" 

34KEY_CHOICES = "choices" 

35KEY_METAVAR = "metavar" 

36KEY_ACTION = "action" 

37 

38OPTIONAL_Y = "\033[33m<Optional>\033[0m" # yellow 

39POSITIONAL_R = "\033[31m<Positional>\033[0m" # red 

40 

41 

42class Arg(enum.Enum): 

43 """The support arg.""" 

44 TASK_DIR = { 

45 KEY_NAME: "task_dir", KEY_CHECKER: ArgChecker.DIR_EXIST, KEY_REQUIRED: False, KEY_METAVAR: " ", 

46 KEY_HELP: f"{OPTIONAL_Y} Specifies the directory for collecting operator build files, GE dump graphs, " 

47 "and TF Adapter dump graphs. If task_dir is not set, these files are not collected by default.", 

48 } 

49 TASK = { 

50 KEY_NAME: "task", KEY_CHECKER: ArgChecker.EXECUTABLE, KEY_REQUIRED: True, KEY_METAVAR: " ", 

51 KEY_HELP: f"{POSITIONAL_R} Specifies the execution command for the service. " 

52 "It collects maintenance and debugging information during command execution." 

53 } 

54 OUTPUT = { 

55 KEY_NAME: "output", KEY_CHECKER: ArgChecker.DIR_CREATE, KEY_REQUIRED: False, KEY_METAVAR: " ", 

56 KEY_HELP: f"{OPTIONAL_Y} Specifies the path to save the command execution results, Default: current dir." 

57 } 

58 TAR = { 

59 KEY_NAME: "tar", KEY_CHECKER: ArgChecker.TAR_CHECK, KEY_REQUIRED: False, KEY_METAVAR: " ", 

60 KEY_HELP: f"{OPTIONAL_Y} Specifies whether to compress the asys result directory into a tar.gz file." 

61 " The original directory is not retained after compression. No compression by default." 

62 } 

63 COLLECT_RUN = { 

64 KEY_NAME: "r", KEY_CHECKER: None, KEY_REQUIRED: False, KEY_CHOICES: ['stacktrace'], 

65 KEY_HELP: f"{OPTIONAL_Y} Specifies the collect logs mode, this parameter must be used together with '--remote'" 

66 " and '--all'. It can be set to 'stacktrace' (send signal to the process specified by remote, " 

67 "and generating the stackcore file). " 

68 "If r is not set, collects existing maintenance and debugging information in the environment." 

69 } 

70 REMOTE = { 

71 KEY_NAME: "remote", KEY_TYPE: int, KEY_CHECKER: None, KEY_REQUIRED: False, KEY_METAVAR: " ", 

72 KEY_HELP: f"{OPTIONAL_Y} Specifies the ID of the process that receives signal, " 

73 "this parameter must be used together with '-r=stacktrace'." 

74 } 

75 ALL = { 

76 KEY_NAME: "all", KEY_CHECKER: None, KEY_REQUIRED: False, KEY_METAVAR: " ", 

77 KEY_HELP: f"{OPTIONAL_Y} Specifies the stackcore files for all tasks, this parameter must be used together with" 

78 " '-r=stacktrace'." 

79 } 

80 QUIET = { 

81 KEY_NAME: "quiet", KEY_CHECKER: None, KEY_REQUIRED: False, KEY_METAVAR: " ", 

82 KEY_HELP: f"{OPTIONAL_Y} Disable the interaction function during stack information export, " 

83 "this parameter must be used together with '-r=stacktrace'." 

84 } 

85 STACKTRACE_TIMEOUT = { 

86 KEY_NAME: "timeout", KEY_TYPE: int, KEY_CHECKER: None, KEY_REQUIRED: False, KEY_METAVAR: " ", 

87 KEY_HELP: f"{OPTIONAL_Y} Specifies the stacktrace collect duration, in seconds, value range: [1, 60]. " 

88 "If this argument is not specified, the default 10s is used." 

89 } 

90 

91 DEVICE = { 

92 KEY_NAME: "d", KEY_TYPE: int, KEY_CHECKER: ArgChecker.DEVICE_ID, KEY_REQUIRED: False, KEY_METAVAR: " ", 

93 KEY_HELP: f"{OPTIONAL_Y} Specifies the ID of the device for command execution." 

94 } 

95 

96 ANALYZE_DEVICE = { 

97 KEY_NAME: "d", KEY_TYPE: int, KEY_CHECKER: ArgChecker.DEVICE_ID, KEY_REQUIRED: False, KEY_METAVAR: " ", 

98 KEY_HELP: f"{OPTIONAL_Y} Specifies the ID of the device for command execution. This argument " 

99 "is valid only for 'aicore_error'." 

100 } 

101 

102 DIS_RUN = { 

103 KEY_NAME: "r", KEY_CHECKER: None, KEY_REQUIRED: True, 

104 KEY_CHOICES: ['stress_detect', 'hbm_detect', 'cpu_detect', 'component'], 

105 KEY_HELP: f"{POSITIONAL_R} Specifies the hardware detection mode. It can be set to 'stress_detect' (AI Core " 

106 "stress test), 'hbm_detect' (HBM detection), 'cpu_detect' (CPU detection) or " 

107 "'component' (Operator detection)." 

108 } 

109 TIMEOUT = { 

110 KEY_NAME: "timeout", KEY_TYPE: int, KEY_CHECKER: None, KEY_REQUIRED: False, KEY_METAVAR: " ", 

111 KEY_HELP: f"{OPTIONAL_Y} Specifies the detection duration, in seconds. " 

112 "In HBM detection mode, value range: [0, 604800]. In CPU detection mode, value range: [1, 604800]. " 

113 "If this argument is not specified, the default 600s is used." 

114 } 

115 

116 INFO_RUN = { 

117 KEY_NAME: "r", KEY_CHECKER: None, KEY_REQUIRED: True, 

118 KEY_CHOICES: ['hardware', 'software', 'status'], 

119 KEY_HELP: f"{POSITIONAL_R} Specifies the type of information to be collected." 

120 " It can be set to 'status' (device information), 'software' (software information of the host), " 

121 "or 'hardware' (hardware information of the host and device)." 

122 } 

123 

124 ANALYZE_RUN = { 

125 KEY_NAME: "r", KEY_CHECKER: None, KEY_REQUIRED: True, 

126 KEY_CHOICES: ["trace", "coredump", "coretrace", "stackcore", "aicore_error", "ub"], 

127 KEY_HELP: f"{POSITIONAL_R} Specifies the type of data to be analyzed. It can be set to 'trace' " 

128 "(trace binary file), 'coredump' (system core file), 'coretrace' (coretrace file), " 

129 "'stackcore' (stackcore file), 'aicore_error' (aicore error dump and log) or " 

130 "'ub' (UB binary files)." 

131 } 

132 FILE = { 

133 KEY_NAME: "file", KEY_CHECKER: ArgChecker.FILE_PATH_EXIST_R, KEY_REQUIRED: False, KEY_METAVAR: " ", 

134 KEY_HELP: f"{POSITIONAL_R} Specifies the single file to be analyzed. This argument is valid only for 'trace', " 

135 "'coretrace' and 'stackcore'. Mutually exclusive with '--path'." 

136 } 

137 PATH = { 

138 KEY_NAME: "path", KEY_CHECKER: ArgChecker.FILE_PATH_EXIST_R, KEY_REQUIRED: False, KEY_METAVAR: " ", 

139 KEY_HELP: f"{POSITIONAL_R} Specifies the path to be analyzed. This argument is valid only for 'trace', " 

140 "'coretrace' and 'stackcore'. Mutually exclusive with '--file'." 

141 } 

142 EXE_FILE = { 

143 KEY_NAME: "exe_file", KEY_CHECKER: None, KEY_REQUIRED: False, KEY_METAVAR: " ", 

144 KEY_HELP: f"{POSITIONAL_R} Specifies the executable file to be debugged. " 

145 "This argument is valid only for 'coredump'." 

146 } 

147 CORE_FILE = { 

148 KEY_NAME: "core_file", KEY_CHECKER: ArgChecker.CORE_FILE, KEY_REQUIRED: False, KEY_METAVAR: " ", 

149 KEY_HELP: f"{POSITIONAL_R} Specifies the core file to be debugged. This argument is valid only for 'coredump'." 

150 } 

151 SYMBOL = { 

152 KEY_NAME: "symbol", KEY_TYPE: int, KEY_CHECKER: None, KEY_REQUIRED: False, KEY_CHOICES: [0, 1], 

153 KEY_HELP: f"{OPTIONAL_Y} Specifies whether to retain the stack frame information that fails to be analyzed " 

154 "in the result (represented by double questions marks '??'). " 

155 "This argument is valid only for 'coredump'. Defaults to 0, indicating not to retain." 

156 } 

157 SYMBOL_PATH = { 

158 KEY_NAME: "symbol_path", KEY_CHECKER: ArgChecker.SYMBOL_PATH, KEY_REQUIRED: False, KEY_METAVAR: " ", 

159 KEY_HELP: f"{OPTIONAL_Y} Specifies the path of executable files and dependent dynamic library files. " 

160 "Subpaths are not searched. This argument is valid only for 'stackcore'. " 

161 "Defaults to the dynamic library path in the stackcore file." 

162 } 

163 REG = { 

164 KEY_NAME: "reg", KEY_TYPE: int, KEY_CHECKER: None, KEY_REQUIRED: False, KEY_CHOICES: [0, 1, 2], 

165 KEY_HELP: f"{OPTIONAL_Y} Specifies the mode of adding register data for analysis. " 

166 "0: not add; 1: add only for threads; 2: add for all stack frames. Defaults to 0." 

167 } 

168 GET = { 

169 KEY_NAME: "get", KEY_CHECKER: None, KEY_REQUIRED: False, KEY_METAVAR: " ", 

170 KEY_HELP: f"{OPTIONAL_Y} Gets the configuration. Use either this argument or '--restore'." 

171 } 

172 RESTORE = { 

173 KEY_NAME: "restore", KEY_CHECKER: None, KEY_REQUIRED: False, KEY_METAVAR: " ", 

174 KEY_HELP: f"{OPTIONAL_Y} Restores the configuration. Use either this argument or '--get'." 

175 } 

176 STRESS_DETECT = { 

177 KEY_NAME: "stress_detect", KEY_CHECKER: None, KEY_REQUIRED: True, KEY_METAVAR: " ", 

178 KEY_HELP: f"{POSITIONAL_R} Specifies the configuration options to be queried or restored, " 

179 "indicating the configurations related to the pressure test." 

180 } 

181 PROFILING_RUN = { 

182 KEY_NAME: "r", KEY_CHECKER: None, KEY_REQUIRED: True, KEY_METAVAR: " ", 

183 KEY_HELP: f"{POSITIONAL_R} Specifies the type of profile information to be collected. " 

184 "It can be set to 'aicore' (aicore information), 'dvpp' (dvpp information), " 

185 "'memory' (hardware memory information), 'link' (interconnection information), " 

186 "'os' (system information), 'power' (low power information), " 

187 "or any combination of these values, separated by ','." 

188 } 

189 PERIOD = { 

190 KEY_NAME: "p", KEY_TYPE: int, KEY_CHECKER: None, KEY_REQUIRED: True, KEY_METAVAR: " ", 

191 KEY_HELP: f"{POSITIONAL_R} Specifies the profile information collection period, in seconds. " 

192 "Value range: [1, 2592000]." 

193 } 

194 AIC_METRICS = { 

195 KEY_NAME: "aic_metrics", KEY_CHECKER: None, KEY_REQUIRED: False, KEY_METAVAR: " ", 

196 KEY_CHOICES: ["PipeUtilization", "ArithmeticUtilization", "Memory", "MemoryL0", 

197 "MemoryUB", "ResourceConflictRatio", "L2Cache", "MemoryAccess"], 

198 KEY_HELP: f"{OPTIONAL_Y} Specifies the aicore metrics to be collected. " 

199 "It takes effect when the run mode includes aicore. It can be set to 'PipeUtilization' " 

200 "(time consumption and proportion of computing unit and handling unit), " 

201 "'ArithmeticUtilization' (time consumption and proportion of cube and vector instructions), " 

202 "'Memory' (Memory IO bandwidth), 'MemoryL0' (L0 IO bandwidth), 'MemoryUB' (UB IO bandwidth), " 

203 "'ResourceConflictRatio' (percentage of pipeline queue class instructions) or " 

204 "'L2Cache' (read/write cache hit count and re-allocation count after misses). " 

205 "'MemoryAccess' (Bandwidth and data volume of operators accessing memory on the aicore). " 

206 "If this argument is not specified, the default PipeUtilization is used." 

207 } 

208 

209 

210class Command(enum.Enum): 

211 """The support command.""" 

212 COLLECT = { 

213 KEY_NAME: "collect", 

214 KEY_ARGS: [Arg.TASK_DIR, Arg.OUTPUT, Arg.TAR, Arg.COLLECT_RUN, Arg.REMOTE, Arg.ALL, Arg.QUIET, 

215 Arg.STACKTRACE_TIMEOUT], 

216 KEY_HELP: "Collects existing maintenance and debugging information in the environment, " 

217 "or export stacktrace information in real time." 

218 } 

219 LAUNCH = { 

220 KEY_NAME: "launch", 

221 KEY_ARGS: [Arg.TASK, Arg.OUTPUT, Arg.TAR], 

222 KEY_HELP: "Executes the script of task parameters, and collects the maintenance " 

223 "and debugging information during the script execution." 

224 } 

225 DIAGNOSE = { 

226 KEY_NAME: "diagnose", 

227 KEY_ARGS: [Arg.DIS_RUN, Arg.DEVICE, Arg.TIMEOUT, Arg.OUTPUT], 

228 KEY_HELP: "Diagnoses the hardware status of the device. It has diagnostic capabilities for " 

229 "component, stress_detect, hbm_detect and cpu_detect. " 

230 "The detect diagnostic only supports [910B, 910_93, 950]. " 

231 } 

232 HEALTH = { 

233 KEY_NAME: "health", 

234 KEY_ARGS: [Arg.DEVICE], 

235 KEY_HELP: "Diagnoses the health status of the device." 

236 } 

237 INFO = { 

238 KEY_NAME: "info", 

239 KEY_ARGS: [Arg.INFO_RUN, Arg.DEVICE], 

240 KEY_HELP: "Collects the software and hardware information of the host and device." 

241 } 

242 ANALYZE = { 

243 KEY_NAME: "analyze", 

244 KEY_ARGS: [Arg.ANALYZE_RUN, Arg.ANALYZE_DEVICE, Arg.FILE, Arg.PATH, Arg.EXE_FILE, Arg.CORE_FILE, Arg.SYMBOL, 

245 Arg.SYMBOL_PATH, Arg.REG, Arg.OUTPUT], 

246 KEY_HELP: "Analyzes the trace, coredump, coretrace, stackcore, aicore_error and ub info." 

247 } 

248 CONFIG = { 

249 KEY_NAME: "config", 

250 KEY_ARGS: [Arg.GET, Arg.DEVICE, Arg.RESTORE, Arg.STRESS_DETECT], 

251 KEY_HELP: "Gets or restores configuration information." 

252 } 

253 PROFILING = { 

254 KEY_NAME: "profiling", 

255 KEY_ARGS: [Arg.DEVICE, Arg.PROFILING_RUN, Arg.OUTPUT, Arg.PERIOD, Arg.AIC_METRICS], 

256 KEY_HELP: "Collects the profiling information of the device." 

257 } 

258 

259 

260class CommandLineParser: 

261 """ 

262 The definition of command line parser. 

263 """ 

264 

265 def __init__(self): 

266 description_msg = textwrap.dedent('''\ 

267 command help: 

268 asys {command} [-h, --help] 

269 ''') 

270 self.parser = argparse.ArgumentParser(prog="asys", formatter_class=argparse.RawDescriptionHelpFormatter, 

271 description=description_msg) 

272 subparsers = self.parser.add_subparsers(dest='subparser_name', help='asys supported commands') 

273 self.__config_parser = None 

274 

275 # Config the parser from Command and Args 

276 for cmd in Command: 

277 cmd_conf = cmd.value 

278 # analyze, diagnose only support EP 

279 if ParamDict().get_env_type() == "RC" and cmd_conf[KEY_NAME] not in [consts.collect_cmd, consts.launch_cmd]: 

280 continue 

281 parser = subparsers.add_parser(cmd_conf[KEY_NAME], help=cmd_conf[KEY_HELP], allow_abbrev=False) 

282 if cmd_conf[KEY_NAME] == consts.config_cmd: 

283 self.__config_parser = parser 

284 self.__set_config_cmd_parser(parser, cmd_conf) 

285 continue 

286 if cmd_conf[KEY_NAME] == consts.analyze_cmd: 

287 self.__set_analyze_cmd_parser(parser, cmd_conf) 

288 continue 

289 

290 supported_args = cmd_conf[KEY_ARGS] 

291 for arg in supported_args: 

292 arg_conf = arg.value 

293 if arg_conf.get(KEY_NAME) in ['d', 'r', 'p']: 

294 arg_name = "-" + arg_conf[KEY_NAME] 

295 else: 

296 arg_name = "--" + arg_conf[KEY_NAME] 

297 _metavar = " " 

298 if arg_conf.get(KEY_CHOICES): 

299 _metavar = None 

300 if arg_conf[KEY_NAME] in ["all", "quiet"]: 

301 parser.add_argument( 

302 arg_name, required=arg_conf[KEY_REQUIRED], action="store_true", help=arg_conf[KEY_HELP] 

303 ) 

304 else: 

305 parser.add_argument( 

306 arg_name, type=arg_conf.get(KEY_TYPE, str), required=arg_conf[KEY_REQUIRED], 

307 choices=arg_conf.get(KEY_CHOICES), help=arg_conf[KEY_HELP], metavar=arg_conf.get(KEY_METAVAR) 

308 ) 

309 

310 @staticmethod 

311 def __set_config_cmd_parser(parser, cmd_conf): 

312 # 不使用 add_mutually_exclusive_group:Python 3.14 的 argparse 会将互斥 

313 # 组成员合并渲染为 "[--get | --restore]",与历史 3.12 风格 "[--get] [-d ] 

314 # [--restore]"(保留参数声明顺序)不一致。这里改为常规 add_argument, 

315 # 在 parse() 阶段手动校验互斥关系,并复用 argparse 错误信息格式。 

316 supported_args = cmd_conf[KEY_ARGS] 

317 for arg in supported_args: 

318 arg_conf = arg.value 

319 arg_name = "--" + arg_conf[KEY_NAME] 

320 if arg_conf.get(KEY_NAME) == 'd': 

321 arg_name = "-" + arg_conf[KEY_NAME] 

322 parser.add_argument( 

323 arg_name, type=arg_conf.get(KEY_TYPE, str), required=arg_conf[KEY_REQUIRED], 

324 choices=arg_conf.get(KEY_CHOICES), help=arg_conf[KEY_HELP], metavar=arg_conf.get(KEY_METAVAR) 

325 ) 

326 continue 

327 

328 parser.add_argument(arg_name, required=arg_conf[KEY_REQUIRED], action="store_true", 

329 help=arg_conf[KEY_HELP]) 

330 

331 @staticmethod 

332 def __set_analyze_cmd_parser(parser, cmd_conf): 

333 group = parser.add_mutually_exclusive_group(required=False) 

334 supported_args = cmd_conf[KEY_ARGS] 

335 for arg in supported_args: 

336 arg_conf = arg.value 

337 arg_name = "--" + arg_conf[KEY_NAME] 

338 if arg_conf.get(KEY_NAME) in ['d', 'r']: 

339 arg_name = "-" + arg_conf[KEY_NAME] 

340 

341 if arg_conf.get(KEY_NAME) in ["file", 'path']: 

342 group.add_argument(arg_name, type=arg_conf.get(KEY_TYPE, str), required=arg_conf[KEY_REQUIRED], 

343 help=arg_conf[KEY_HELP], metavar=arg_conf.get(KEY_METAVAR)) 

344 else: 

345 parser.add_argument( 

346 arg_name, type=arg_conf.get(KEY_TYPE, str), required=arg_conf[KEY_REQUIRED], 

347 choices=arg_conf.get(KEY_CHOICES), help=arg_conf[KEY_HELP], metavar=arg_conf.get(KEY_METAVAR) 

348 ) 

349 

350 def print_help(self): 

351 """Print the help information generated by parser""" 

352 self.parser.print_help() 

353 

354 @classmethod 

355 def check_arg_with_checker(cls, arg_name, arg_val, checker): 

356 """ 

357 Check arg with checker. 

358 

359 Args: 

360 arg_val: The value of arg to check 

361 checker: The check function to use 

362 

363 Returns: 

364 RetCode: return code (SUCCESS:0, FAILED:1) 

365 """ 

366 if checker is None: 

367 return RetCode.SUCCESS 

368 return checker(arg_name, arg_val) 

369 

370 @classmethod 

371 def match_command(cls, cmd): 

372 """ 

373 Match input command to Enum cmd type. 

374 

375 Args: 

376 cmd: Input command 

377 

378 Returns: 

379 command: Enum cmd type 

380 """ 

381 for command in Command: 

382 command_conf = command.value 

383 if command_conf[KEY_NAME] == cmd: 

384 return command 

385 return None 

386 

387 @classmethod 

388 def check_args(cls, args): 

389 """ 

390 Check args according to command type. 

391 

392 Args: 

393 args: The namesapce returned by parse_args 

394 

395 Returns: 

396 RetCode: return code (SUCCESS:0, FAILED:1) 

397 """ 

398 input_cmd = args.subparser_name 

399 command = CommandLineParser.match_command(input_cmd) 

400 if not command: 

401 return RetCode.FAILED 

402 supported_args = command.value[KEY_ARGS] 

403 for support_arg in supported_args: 

404 arg_info = support_arg.value 

405 arg_val = getattr(args, arg_info[KEY_NAME]) 

406 if arg_val is None: 

407 continue # this arg is optional and not set, check next arg 

408 checker = arg_info[KEY_CHECKER] 

409 ret = CommandLineParser.check_arg_with_checker(arg_info[KEY_NAME], arg_val, checker) 

410 if ret != RetCode.SUCCESS: 

411 return RetCode.FAILED 

412 return RetCode.SUCCESS 

413 

414 def parse(self): 

415 """ 

416 Parse the command and args from cmd line. 

417 

418 Returns: 

419 RetCode: return code (SUCCESS:0, FAILED:1) 

420 """ 

421 # config 子命令的 --get / --restore 互斥校验:原本通过 argparse 的 

422 # mutually_exclusive_group 实现,但 3.14 改变了 usage 渲染顺序,故改为 

423 # 手动校验。在 parse_args 之前预扫描 argv,确保互斥错误优先于 required 

424 # 缺失错误抛出,与历史 mutex group 行为一致;错误信息复用 

425 # subparser.error(),与原 argparse 输出格式完全相同。 

426 self.__check_config_mutex_in_argv() 

427 args = self.parser.parse_args() 

428 if args.subparser_name is None: # -h, --help, and only asys 

429 return RetCode.SUCCESS 

430 if CommandLineParser.check_args(args) == RetCode.FAILED: 

431 return RetCode.FAILED 

432 ParamDict().set_args(args) 

433 return RetCode.SUCCESS 

434 

435 def __check_config_mutex_in_argv(self): 

436 if self.__config_parser is None: 

437 return 

438 argv = sys.argv[1:] 

439 if not argv or argv[0] != consts.config_cmd: 

440 return 

441 sub_argv = argv[1:] 

442 has_get = any(tok == "--get" or tok.startswith("--get=") for tok in sub_argv) 

443 has_restore = any(tok == "--restore" or tok.startswith("--restore=") for tok in sub_argv) 

444 if has_get and has_restore: 

445 self.__config_parser.error("argument --restore: not allowed with argument --get")