| 1 | /**
|
|---|
| 2 | * class ArgumentParser
|
|---|
| 3 | *
|
|---|
| 4 | * Object for parsing command line strings into js objects.
|
|---|
| 5 | *
|
|---|
| 6 | * Inherited from [[ActionContainer]]
|
|---|
| 7 | **/
|
|---|
| 8 | 'use strict';
|
|---|
| 9 |
|
|---|
| 10 | var util = require('util');
|
|---|
| 11 | var format = require('util').format;
|
|---|
| 12 | var Path = require('path');
|
|---|
| 13 | var sprintf = require('sprintf-js').sprintf;
|
|---|
| 14 |
|
|---|
| 15 | // Constants
|
|---|
| 16 | var c = require('./const');
|
|---|
| 17 |
|
|---|
| 18 | var $$ = require('./utils');
|
|---|
| 19 |
|
|---|
| 20 | var ActionContainer = require('./action_container');
|
|---|
| 21 |
|
|---|
| 22 | // Errors
|
|---|
| 23 | var argumentErrorHelper = require('./argument/error');
|
|---|
| 24 |
|
|---|
| 25 | var HelpFormatter = require('./help/formatter');
|
|---|
| 26 |
|
|---|
| 27 | var Namespace = require('./namespace');
|
|---|
| 28 |
|
|---|
| 29 |
|
|---|
| 30 | /**
|
|---|
| 31 | * new ArgumentParser(options)
|
|---|
| 32 | *
|
|---|
| 33 | * Create a new ArgumentParser object.
|
|---|
| 34 | *
|
|---|
| 35 | * ##### Options:
|
|---|
| 36 | * - `prog` The name of the program (default: Path.basename(process.argv[1]))
|
|---|
| 37 | * - `usage` A usage message (default: auto-generated from arguments)
|
|---|
| 38 | * - `description` A description of what the program does
|
|---|
| 39 | * - `epilog` Text following the argument descriptions
|
|---|
| 40 | * - `parents` Parsers whose arguments should be copied into this one
|
|---|
| 41 | * - `formatterClass` HelpFormatter class for printing help messages
|
|---|
| 42 | * - `prefixChars` Characters that prefix optional arguments
|
|---|
| 43 | * - `fromfilePrefixChars` Characters that prefix files containing additional arguments
|
|---|
| 44 | * - `argumentDefault` The default value for all arguments
|
|---|
| 45 | * - `addHelp` Add a -h/-help option
|
|---|
| 46 | * - `conflictHandler` Specifies how to handle conflicting argument names
|
|---|
| 47 | * - `debug` Enable debug mode. Argument errors throw exception in
|
|---|
| 48 | * debug mode and process.exit in normal. Used for development and
|
|---|
| 49 | * testing (default: false)
|
|---|
| 50 | *
|
|---|
| 51 | * See also [original guide][1]
|
|---|
| 52 | *
|
|---|
| 53 | * [1]:http://docs.python.org/dev/library/argparse.html#argumentparser-objects
|
|---|
| 54 | **/
|
|---|
| 55 | function ArgumentParser(options) {
|
|---|
| 56 | if (!(this instanceof ArgumentParser)) {
|
|---|
| 57 | return new ArgumentParser(options);
|
|---|
| 58 | }
|
|---|
| 59 | var self = this;
|
|---|
| 60 | options = options || {};
|
|---|
| 61 |
|
|---|
| 62 | options.description = (options.description || null);
|
|---|
| 63 | options.argumentDefault = (options.argumentDefault || null);
|
|---|
| 64 | options.prefixChars = (options.prefixChars || '-');
|
|---|
| 65 | options.conflictHandler = (options.conflictHandler || 'error');
|
|---|
| 66 | ActionContainer.call(this, options);
|
|---|
| 67 |
|
|---|
| 68 | options.addHelp = typeof options.addHelp === 'undefined' || !!options.addHelp;
|
|---|
| 69 | options.parents = options.parents || [];
|
|---|
| 70 | // default program name
|
|---|
| 71 | options.prog = (options.prog || Path.basename(process.argv[1]));
|
|---|
| 72 | this.prog = options.prog;
|
|---|
| 73 | this.usage = options.usage;
|
|---|
| 74 | this.epilog = options.epilog;
|
|---|
| 75 | this.version = options.version;
|
|---|
| 76 |
|
|---|
| 77 | this.debug = (options.debug === true);
|
|---|
| 78 |
|
|---|
| 79 | this.formatterClass = (options.formatterClass || HelpFormatter);
|
|---|
| 80 | this.fromfilePrefixChars = options.fromfilePrefixChars || null;
|
|---|
| 81 | this._positionals = this.addArgumentGroup({ title: 'Positional arguments' });
|
|---|
| 82 | this._optionals = this.addArgumentGroup({ title: 'Optional arguments' });
|
|---|
| 83 | this._subparsers = null;
|
|---|
| 84 |
|
|---|
| 85 | // register types
|
|---|
| 86 | function FUNCTION_IDENTITY(o) {
|
|---|
| 87 | return o;
|
|---|
| 88 | }
|
|---|
| 89 | this.register('type', 'auto', FUNCTION_IDENTITY);
|
|---|
| 90 | this.register('type', null, FUNCTION_IDENTITY);
|
|---|
| 91 | this.register('type', 'int', function (x) {
|
|---|
| 92 | var result = parseInt(x, 10);
|
|---|
| 93 | if (isNaN(result)) {
|
|---|
| 94 | throw new Error(x + ' is not a valid integer.');
|
|---|
| 95 | }
|
|---|
| 96 | return result;
|
|---|
| 97 | });
|
|---|
| 98 | this.register('type', 'float', function (x) {
|
|---|
| 99 | var result = parseFloat(x);
|
|---|
| 100 | if (isNaN(result)) {
|
|---|
| 101 | throw new Error(x + ' is not a valid float.');
|
|---|
| 102 | }
|
|---|
| 103 | return result;
|
|---|
| 104 | });
|
|---|
| 105 | this.register('type', 'string', function (x) {
|
|---|
| 106 | return '' + x;
|
|---|
| 107 | });
|
|---|
| 108 |
|
|---|
| 109 | // add help and version arguments if necessary
|
|---|
| 110 | var defaultPrefix = (this.prefixChars.indexOf('-') > -1) ? '-' : this.prefixChars[0];
|
|---|
| 111 | if (options.addHelp) {
|
|---|
| 112 | this.addArgument(
|
|---|
| 113 | [ defaultPrefix + 'h', defaultPrefix + defaultPrefix + 'help' ],
|
|---|
| 114 | {
|
|---|
| 115 | action: 'help',
|
|---|
| 116 | defaultValue: c.SUPPRESS,
|
|---|
| 117 | help: 'Show this help message and exit.'
|
|---|
| 118 | }
|
|---|
| 119 | );
|
|---|
| 120 | }
|
|---|
| 121 | if (typeof this.version !== 'undefined') {
|
|---|
| 122 | this.addArgument(
|
|---|
| 123 | [ defaultPrefix + 'v', defaultPrefix + defaultPrefix + 'version' ],
|
|---|
| 124 | {
|
|---|
| 125 | action: 'version',
|
|---|
| 126 | version: this.version,
|
|---|
| 127 | defaultValue: c.SUPPRESS,
|
|---|
| 128 | help: "Show program's version number and exit."
|
|---|
| 129 | }
|
|---|
| 130 | );
|
|---|
| 131 | }
|
|---|
| 132 |
|
|---|
| 133 | // add parent arguments and defaults
|
|---|
| 134 | options.parents.forEach(function (parent) {
|
|---|
| 135 | self._addContainerActions(parent);
|
|---|
| 136 | if (typeof parent._defaults !== 'undefined') {
|
|---|
| 137 | for (var defaultKey in parent._defaults) {
|
|---|
| 138 | if (parent._defaults.hasOwnProperty(defaultKey)) {
|
|---|
| 139 | self._defaults[defaultKey] = parent._defaults[defaultKey];
|
|---|
| 140 | }
|
|---|
| 141 | }
|
|---|
| 142 | }
|
|---|
| 143 | });
|
|---|
| 144 | }
|
|---|
| 145 |
|
|---|
| 146 | util.inherits(ArgumentParser, ActionContainer);
|
|---|
| 147 |
|
|---|
| 148 | /**
|
|---|
| 149 | * ArgumentParser#addSubparsers(options) -> [[ActionSubparsers]]
|
|---|
| 150 | * - options (object): hash of options see [[ActionSubparsers.new]]
|
|---|
| 151 | *
|
|---|
| 152 | * See also [subcommands][1]
|
|---|
| 153 | *
|
|---|
| 154 | * [1]:http://docs.python.org/dev/library/argparse.html#sub-commands
|
|---|
| 155 | **/
|
|---|
| 156 | ArgumentParser.prototype.addSubparsers = function (options) {
|
|---|
| 157 | if (this._subparsers) {
|
|---|
| 158 | this.error('Cannot have multiple subparser arguments.');
|
|---|
| 159 | }
|
|---|
| 160 |
|
|---|
| 161 | options = options || {};
|
|---|
| 162 | options.debug = (this.debug === true);
|
|---|
| 163 | options.optionStrings = [];
|
|---|
| 164 | options.parserClass = (options.parserClass || ArgumentParser);
|
|---|
| 165 |
|
|---|
| 166 |
|
|---|
| 167 | if (!!options.title || !!options.description) {
|
|---|
| 168 |
|
|---|
| 169 | this._subparsers = this.addArgumentGroup({
|
|---|
| 170 | title: (options.title || 'subcommands'),
|
|---|
| 171 | description: options.description
|
|---|
| 172 | });
|
|---|
| 173 | delete options.title;
|
|---|
| 174 | delete options.description;
|
|---|
| 175 |
|
|---|
| 176 | } else {
|
|---|
| 177 | this._subparsers = this._positionals;
|
|---|
| 178 | }
|
|---|
| 179 |
|
|---|
| 180 | // prog defaults to the usage message of this parser, skipping
|
|---|
| 181 | // optional arguments and with no "usage:" prefix
|
|---|
| 182 | if (!options.prog) {
|
|---|
| 183 | var formatter = this._getFormatter();
|
|---|
| 184 | var positionals = this._getPositionalActions();
|
|---|
| 185 | var groups = this._mutuallyExclusiveGroups;
|
|---|
| 186 | formatter.addUsage(this.usage, positionals, groups, '');
|
|---|
| 187 | options.prog = formatter.formatHelp().trim();
|
|---|
| 188 | }
|
|---|
| 189 |
|
|---|
| 190 | // create the parsers action and add it to the positionals list
|
|---|
| 191 | var ParsersClass = this._popActionClass(options, 'parsers');
|
|---|
| 192 | var action = new ParsersClass(options);
|
|---|
| 193 | this._subparsers._addAction(action);
|
|---|
| 194 |
|
|---|
| 195 | // return the created parsers action
|
|---|
| 196 | return action;
|
|---|
| 197 | };
|
|---|
| 198 |
|
|---|
| 199 | ArgumentParser.prototype._addAction = function (action) {
|
|---|
| 200 | if (action.isOptional()) {
|
|---|
| 201 | this._optionals._addAction(action);
|
|---|
| 202 | } else {
|
|---|
| 203 | this._positionals._addAction(action);
|
|---|
| 204 | }
|
|---|
| 205 | return action;
|
|---|
| 206 | };
|
|---|
| 207 |
|
|---|
| 208 | ArgumentParser.prototype._getOptionalActions = function () {
|
|---|
| 209 | return this._actions.filter(function (action) {
|
|---|
| 210 | return action.isOptional();
|
|---|
| 211 | });
|
|---|
| 212 | };
|
|---|
| 213 |
|
|---|
| 214 | ArgumentParser.prototype._getPositionalActions = function () {
|
|---|
| 215 | return this._actions.filter(function (action) {
|
|---|
| 216 | return action.isPositional();
|
|---|
| 217 | });
|
|---|
| 218 | };
|
|---|
| 219 |
|
|---|
| 220 |
|
|---|
| 221 | /**
|
|---|
| 222 | * ArgumentParser#parseArgs(args, namespace) -> Namespace|Object
|
|---|
| 223 | * - args (array): input elements
|
|---|
| 224 | * - namespace (Namespace|Object): result object
|
|---|
| 225 | *
|
|---|
| 226 | * Parsed args and throws error if some arguments are not recognized
|
|---|
| 227 | *
|
|---|
| 228 | * See also [original guide][1]
|
|---|
| 229 | *
|
|---|
| 230 | * [1]:http://docs.python.org/dev/library/argparse.html#the-parse-args-method
|
|---|
| 231 | **/
|
|---|
| 232 | ArgumentParser.prototype.parseArgs = function (args, namespace) {
|
|---|
| 233 | var argv;
|
|---|
| 234 | var result = this.parseKnownArgs(args, namespace);
|
|---|
| 235 |
|
|---|
| 236 | args = result[0];
|
|---|
| 237 | argv = result[1];
|
|---|
| 238 | if (argv && argv.length > 0) {
|
|---|
| 239 | this.error(
|
|---|
| 240 | format('Unrecognized arguments: %s.', argv.join(' '))
|
|---|
| 241 | );
|
|---|
| 242 | }
|
|---|
| 243 | return args;
|
|---|
| 244 | };
|
|---|
| 245 |
|
|---|
| 246 | /**
|
|---|
| 247 | * ArgumentParser#parseKnownArgs(args, namespace) -> array
|
|---|
| 248 | * - args (array): input options
|
|---|
| 249 | * - namespace (Namespace|Object): result object
|
|---|
| 250 | *
|
|---|
| 251 | * Parse known arguments and return tuple of result object
|
|---|
| 252 | * and unknown args
|
|---|
| 253 | *
|
|---|
| 254 | * See also [original guide][1]
|
|---|
| 255 | *
|
|---|
| 256 | * [1]:http://docs.python.org/dev/library/argparse.html#partial-parsing
|
|---|
| 257 | **/
|
|---|
| 258 | ArgumentParser.prototype.parseKnownArgs = function (args, namespace) {
|
|---|
| 259 | var self = this;
|
|---|
| 260 |
|
|---|
| 261 | // args default to the system args
|
|---|
| 262 | args = args || process.argv.slice(2);
|
|---|
| 263 |
|
|---|
| 264 | // default Namespace built from parser defaults
|
|---|
| 265 | namespace = namespace || new Namespace();
|
|---|
| 266 |
|
|---|
| 267 | self._actions.forEach(function (action) {
|
|---|
| 268 | if (action.dest !== c.SUPPRESS) {
|
|---|
| 269 | if (!$$.has(namespace, action.dest)) {
|
|---|
| 270 | if (action.defaultValue !== c.SUPPRESS) {
|
|---|
| 271 | var defaultValue = action.defaultValue;
|
|---|
| 272 | if (typeof action.defaultValue === 'string') {
|
|---|
| 273 | defaultValue = self._getValue(action, defaultValue);
|
|---|
| 274 | }
|
|---|
| 275 | namespace[action.dest] = defaultValue;
|
|---|
| 276 | }
|
|---|
| 277 | }
|
|---|
| 278 | }
|
|---|
| 279 | });
|
|---|
| 280 |
|
|---|
| 281 | Object.keys(self._defaults).forEach(function (dest) {
|
|---|
| 282 | namespace[dest] = self._defaults[dest];
|
|---|
| 283 | });
|
|---|
| 284 |
|
|---|
| 285 | // parse the arguments and exit if there are any errors
|
|---|
| 286 | try {
|
|---|
| 287 | var res = this._parseKnownArgs(args, namespace);
|
|---|
| 288 |
|
|---|
| 289 | namespace = res[0];
|
|---|
| 290 | args = res[1];
|
|---|
| 291 | if ($$.has(namespace, c._UNRECOGNIZED_ARGS_ATTR)) {
|
|---|
| 292 | args = $$.arrayUnion(args, namespace[c._UNRECOGNIZED_ARGS_ATTR]);
|
|---|
| 293 | delete namespace[c._UNRECOGNIZED_ARGS_ATTR];
|
|---|
| 294 | }
|
|---|
| 295 | return [ namespace, args ];
|
|---|
| 296 | } catch (e) {
|
|---|
| 297 | this.error(e);
|
|---|
| 298 | }
|
|---|
| 299 | };
|
|---|
| 300 |
|
|---|
| 301 | ArgumentParser.prototype._parseKnownArgs = function (argStrings, namespace) {
|
|---|
| 302 | var self = this;
|
|---|
| 303 |
|
|---|
| 304 | var extras = [];
|
|---|
| 305 |
|
|---|
| 306 | // replace arg strings that are file references
|
|---|
| 307 | if (this.fromfilePrefixChars !== null) {
|
|---|
| 308 | argStrings = this._readArgsFromFiles(argStrings);
|
|---|
| 309 | }
|
|---|
| 310 | // map all mutually exclusive arguments to the other arguments
|
|---|
| 311 | // they can't occur with
|
|---|
| 312 | // Python has 'conflicts = action_conflicts.setdefault(mutex_action, [])'
|
|---|
| 313 | // though I can't conceive of a way in which an action could be a member
|
|---|
| 314 | // of two different mutually exclusive groups.
|
|---|
| 315 |
|
|---|
| 316 | function actionHash(action) {
|
|---|
| 317 | // some sort of hashable key for this action
|
|---|
| 318 | // action itself cannot be a key in actionConflicts
|
|---|
| 319 | // I think getName() (join of optionStrings) is unique enough
|
|---|
| 320 | return action.getName();
|
|---|
| 321 | }
|
|---|
| 322 |
|
|---|
| 323 | var conflicts, key;
|
|---|
| 324 | var actionConflicts = {};
|
|---|
| 325 |
|
|---|
| 326 | this._mutuallyExclusiveGroups.forEach(function (mutexGroup) {
|
|---|
| 327 | mutexGroup._groupActions.forEach(function (mutexAction, i, groupActions) {
|
|---|
| 328 | key = actionHash(mutexAction);
|
|---|
| 329 | if (!$$.has(actionConflicts, key)) {
|
|---|
| 330 | actionConflicts[key] = [];
|
|---|
| 331 | }
|
|---|
| 332 | conflicts = actionConflicts[key];
|
|---|
| 333 | conflicts.push.apply(conflicts, groupActions.slice(0, i));
|
|---|
| 334 | conflicts.push.apply(conflicts, groupActions.slice(i + 1));
|
|---|
| 335 | });
|
|---|
| 336 | });
|
|---|
| 337 |
|
|---|
| 338 | // find all option indices, and determine the arg_string_pattern
|
|---|
| 339 | // which has an 'O' if there is an option at an index,
|
|---|
| 340 | // an 'A' if there is an argument, or a '-' if there is a '--'
|
|---|
| 341 | var optionStringIndices = {};
|
|---|
| 342 |
|
|---|
| 343 | var argStringPatternParts = [];
|
|---|
| 344 |
|
|---|
| 345 | argStrings.forEach(function (argString, argStringIndex) {
|
|---|
| 346 | if (argString === '--') {
|
|---|
| 347 | argStringPatternParts.push('-');
|
|---|
| 348 | while (argStringIndex < argStrings.length) {
|
|---|
| 349 | argStringPatternParts.push('A');
|
|---|
| 350 | argStringIndex++;
|
|---|
| 351 | }
|
|---|
| 352 | } else {
|
|---|
| 353 | // otherwise, add the arg to the arg strings
|
|---|
| 354 | // and note the index if it was an option
|
|---|
| 355 | var pattern;
|
|---|
| 356 | var optionTuple = self._parseOptional(argString);
|
|---|
| 357 | if (!optionTuple) {
|
|---|
| 358 | pattern = 'A';
|
|---|
| 359 | } else {
|
|---|
| 360 | optionStringIndices[argStringIndex] = optionTuple;
|
|---|
| 361 | pattern = 'O';
|
|---|
| 362 | }
|
|---|
| 363 | argStringPatternParts.push(pattern);
|
|---|
| 364 | }
|
|---|
| 365 | });
|
|---|
| 366 | var argStringsPattern = argStringPatternParts.join('');
|
|---|
| 367 |
|
|---|
| 368 | var seenActions = [];
|
|---|
| 369 | var seenNonDefaultActions = [];
|
|---|
| 370 |
|
|---|
| 371 |
|
|---|
| 372 | function takeAction(action, argumentStrings, optionString) {
|
|---|
| 373 | seenActions.push(action);
|
|---|
| 374 | var argumentValues = self._getValues(action, argumentStrings);
|
|---|
| 375 |
|
|---|
| 376 | // error if this argument is not allowed with other previously
|
|---|
| 377 | // seen arguments, assuming that actions that use the default
|
|---|
| 378 | // value don't really count as "present"
|
|---|
| 379 | if (argumentValues !== action.defaultValue) {
|
|---|
| 380 | seenNonDefaultActions.push(action);
|
|---|
| 381 | if (actionConflicts[actionHash(action)]) {
|
|---|
| 382 | actionConflicts[actionHash(action)].forEach(function (actionConflict) {
|
|---|
| 383 | if (seenNonDefaultActions.indexOf(actionConflict) >= 0) {
|
|---|
| 384 | throw argumentErrorHelper(
|
|---|
| 385 | action,
|
|---|
| 386 | format('Not allowed with argument "%s".', actionConflict.getName())
|
|---|
| 387 | );
|
|---|
| 388 | }
|
|---|
| 389 | });
|
|---|
| 390 | }
|
|---|
| 391 | }
|
|---|
| 392 |
|
|---|
| 393 | if (argumentValues !== c.SUPPRESS) {
|
|---|
| 394 | action.call(self, namespace, argumentValues, optionString);
|
|---|
| 395 | }
|
|---|
| 396 | }
|
|---|
| 397 |
|
|---|
| 398 | function consumeOptional(startIndex) {
|
|---|
| 399 | // get the optional identified at this index
|
|---|
| 400 | var optionTuple = optionStringIndices[startIndex];
|
|---|
| 401 | var action = optionTuple[0];
|
|---|
| 402 | var optionString = optionTuple[1];
|
|---|
| 403 | var explicitArg = optionTuple[2];
|
|---|
| 404 |
|
|---|
| 405 | // identify additional optionals in the same arg string
|
|---|
| 406 | // (e.g. -xyz is the same as -x -y -z if no args are required)
|
|---|
| 407 | var actionTuples = [];
|
|---|
| 408 |
|
|---|
| 409 | var args, argCount, start, stop;
|
|---|
| 410 |
|
|---|
| 411 | for (;;) {
|
|---|
| 412 | if (!action) {
|
|---|
| 413 | extras.push(argStrings[startIndex]);
|
|---|
| 414 | return startIndex + 1;
|
|---|
| 415 | }
|
|---|
| 416 | if (explicitArg) {
|
|---|
| 417 | argCount = self._matchArgument(action, 'A');
|
|---|
| 418 |
|
|---|
| 419 | // if the action is a single-dash option and takes no
|
|---|
| 420 | // arguments, try to parse more single-dash options out
|
|---|
| 421 | // of the tail of the option string
|
|---|
| 422 | var chars = self.prefixChars;
|
|---|
| 423 | if (argCount === 0 && chars.indexOf(optionString[1]) < 0) {
|
|---|
| 424 | actionTuples.push([ action, [], optionString ]);
|
|---|
| 425 | optionString = optionString[0] + explicitArg[0];
|
|---|
| 426 | var newExplicitArg = explicitArg.slice(1) || null;
|
|---|
| 427 | var optionalsMap = self._optionStringActions;
|
|---|
| 428 |
|
|---|
| 429 | if (Object.keys(optionalsMap).indexOf(optionString) >= 0) {
|
|---|
| 430 | action = optionalsMap[optionString];
|
|---|
| 431 | explicitArg = newExplicitArg;
|
|---|
| 432 | } else {
|
|---|
| 433 | throw argumentErrorHelper(action, sprintf('ignored explicit argument %r', explicitArg));
|
|---|
| 434 | }
|
|---|
| 435 | } else if (argCount === 1) {
|
|---|
| 436 | // if the action expect exactly one argument, we've
|
|---|
| 437 | // successfully matched the option; exit the loop
|
|---|
| 438 | stop = startIndex + 1;
|
|---|
| 439 | args = [ explicitArg ];
|
|---|
| 440 | actionTuples.push([ action, args, optionString ]);
|
|---|
| 441 | break;
|
|---|
| 442 | } else {
|
|---|
| 443 | // error if a double-dash option did not use the
|
|---|
| 444 | // explicit argument
|
|---|
| 445 | throw argumentErrorHelper(action, sprintf('ignored explicit argument %r', explicitArg));
|
|---|
| 446 | }
|
|---|
| 447 | } else {
|
|---|
| 448 | // if there is no explicit argument, try to match the
|
|---|
| 449 | // optional's string arguments with the following strings
|
|---|
| 450 | // if successful, exit the loop
|
|---|
| 451 |
|
|---|
| 452 | start = startIndex + 1;
|
|---|
| 453 | var selectedPatterns = argStringsPattern.substr(start);
|
|---|
| 454 |
|
|---|
| 455 | argCount = self._matchArgument(action, selectedPatterns);
|
|---|
| 456 | stop = start + argCount;
|
|---|
| 457 |
|
|---|
| 458 |
|
|---|
| 459 | args = argStrings.slice(start, stop);
|
|---|
| 460 |
|
|---|
| 461 | actionTuples.push([ action, args, optionString ]);
|
|---|
| 462 | break;
|
|---|
| 463 | }
|
|---|
| 464 |
|
|---|
| 465 | }
|
|---|
| 466 |
|
|---|
| 467 | // add the Optional to the list and return the index at which
|
|---|
| 468 | // the Optional's string args stopped
|
|---|
| 469 | if (actionTuples.length < 1) {
|
|---|
| 470 | throw new Error('length should be > 0');
|
|---|
| 471 | }
|
|---|
| 472 | for (var i = 0; i < actionTuples.length; i++) {
|
|---|
| 473 | takeAction.apply(self, actionTuples[i]);
|
|---|
| 474 | }
|
|---|
| 475 | return stop;
|
|---|
| 476 | }
|
|---|
| 477 |
|
|---|
| 478 | // the list of Positionals left to be parsed; this is modified
|
|---|
| 479 | // by consume_positionals()
|
|---|
| 480 | var positionals = self._getPositionalActions();
|
|---|
| 481 |
|
|---|
| 482 | function consumePositionals(startIndex) {
|
|---|
| 483 | // match as many Positionals as possible
|
|---|
| 484 | var selectedPattern = argStringsPattern.substr(startIndex);
|
|---|
| 485 | var argCounts = self._matchArgumentsPartial(positionals, selectedPattern);
|
|---|
| 486 |
|
|---|
| 487 | // slice off the appropriate arg strings for each Positional
|
|---|
| 488 | // and add the Positional and its args to the list
|
|---|
| 489 | for (var i = 0; i < positionals.length; i++) {
|
|---|
| 490 | var action = positionals[i];
|
|---|
| 491 | var argCount = argCounts[i];
|
|---|
| 492 | if (typeof argCount === 'undefined') {
|
|---|
| 493 | continue;
|
|---|
| 494 | }
|
|---|
| 495 | var args = argStrings.slice(startIndex, startIndex + argCount);
|
|---|
| 496 |
|
|---|
| 497 | startIndex += argCount;
|
|---|
| 498 | takeAction(action, args);
|
|---|
| 499 | }
|
|---|
| 500 |
|
|---|
| 501 | // slice off the Positionals that we just parsed and return the
|
|---|
| 502 | // index at which the Positionals' string args stopped
|
|---|
| 503 | positionals = positionals.slice(argCounts.length);
|
|---|
| 504 | return startIndex;
|
|---|
| 505 | }
|
|---|
| 506 |
|
|---|
| 507 | // consume Positionals and Optionals alternately, until we have
|
|---|
| 508 | // passed the last option string
|
|---|
| 509 | var startIndex = 0;
|
|---|
| 510 | var position;
|
|---|
| 511 |
|
|---|
| 512 | var maxOptionStringIndex = -1;
|
|---|
| 513 |
|
|---|
| 514 | Object.keys(optionStringIndices).forEach(function (position) {
|
|---|
| 515 | maxOptionStringIndex = Math.max(maxOptionStringIndex, parseInt(position, 10));
|
|---|
| 516 | });
|
|---|
| 517 |
|
|---|
| 518 | var positionalsEndIndex, nextOptionStringIndex;
|
|---|
| 519 |
|
|---|
| 520 | while (startIndex <= maxOptionStringIndex) {
|
|---|
| 521 | // consume any Positionals preceding the next option
|
|---|
| 522 | nextOptionStringIndex = null;
|
|---|
| 523 | for (position in optionStringIndices) {
|
|---|
| 524 | if (!optionStringIndices.hasOwnProperty(position)) { continue; }
|
|---|
| 525 |
|
|---|
| 526 | position = parseInt(position, 10);
|
|---|
| 527 | if (position >= startIndex) {
|
|---|
| 528 | if (nextOptionStringIndex !== null) {
|
|---|
| 529 | nextOptionStringIndex = Math.min(nextOptionStringIndex, position);
|
|---|
| 530 | } else {
|
|---|
| 531 | nextOptionStringIndex = position;
|
|---|
| 532 | }
|
|---|
| 533 | }
|
|---|
| 534 | }
|
|---|
| 535 |
|
|---|
| 536 | if (startIndex !== nextOptionStringIndex) {
|
|---|
| 537 | positionalsEndIndex = consumePositionals(startIndex);
|
|---|
| 538 | // only try to parse the next optional if we didn't consume
|
|---|
| 539 | // the option string during the positionals parsing
|
|---|
| 540 | if (positionalsEndIndex > startIndex) {
|
|---|
| 541 | startIndex = positionalsEndIndex;
|
|---|
| 542 | continue;
|
|---|
| 543 | } else {
|
|---|
| 544 | startIndex = positionalsEndIndex;
|
|---|
| 545 | }
|
|---|
| 546 | }
|
|---|
| 547 |
|
|---|
| 548 | // if we consumed all the positionals we could and we're not
|
|---|
| 549 | // at the index of an option string, there were extra arguments
|
|---|
| 550 | if (!optionStringIndices[startIndex]) {
|
|---|
| 551 | var strings = argStrings.slice(startIndex, nextOptionStringIndex);
|
|---|
| 552 | extras = extras.concat(strings);
|
|---|
| 553 | startIndex = nextOptionStringIndex;
|
|---|
| 554 | }
|
|---|
| 555 | // consume the next optional and any arguments for it
|
|---|
| 556 | startIndex = consumeOptional(startIndex);
|
|---|
| 557 | }
|
|---|
| 558 |
|
|---|
| 559 | // consume any positionals following the last Optional
|
|---|
| 560 | var stopIndex = consumePositionals(startIndex);
|
|---|
| 561 |
|
|---|
| 562 | // if we didn't consume all the argument strings, there were extras
|
|---|
| 563 | extras = extras.concat(argStrings.slice(stopIndex));
|
|---|
| 564 |
|
|---|
| 565 | // if we didn't use all the Positional objects, there were too few
|
|---|
| 566 | // arg strings supplied.
|
|---|
| 567 | if (positionals.length > 0) {
|
|---|
| 568 | self.error('too few arguments');
|
|---|
| 569 | }
|
|---|
| 570 |
|
|---|
| 571 | // make sure all required actions were present
|
|---|
| 572 | self._actions.forEach(function (action) {
|
|---|
| 573 | if (action.required) {
|
|---|
| 574 | if (seenActions.indexOf(action) < 0) {
|
|---|
| 575 | self.error(format('Argument "%s" is required', action.getName()));
|
|---|
| 576 | }
|
|---|
| 577 | }
|
|---|
| 578 | });
|
|---|
| 579 |
|
|---|
| 580 | // make sure all required groups have one option present
|
|---|
| 581 | var actionUsed = false;
|
|---|
| 582 | self._mutuallyExclusiveGroups.forEach(function (group) {
|
|---|
| 583 | if (group.required) {
|
|---|
| 584 | actionUsed = group._groupActions.some(function (action) {
|
|---|
| 585 | return seenNonDefaultActions.indexOf(action) !== -1;
|
|---|
| 586 | });
|
|---|
| 587 |
|
|---|
| 588 | // if no actions were used, report the error
|
|---|
| 589 | if (!actionUsed) {
|
|---|
| 590 | var names = [];
|
|---|
| 591 | group._groupActions.forEach(function (action) {
|
|---|
| 592 | if (action.help !== c.SUPPRESS) {
|
|---|
| 593 | names.push(action.getName());
|
|---|
| 594 | }
|
|---|
| 595 | });
|
|---|
| 596 | names = names.join(' ');
|
|---|
| 597 | var msg = 'one of the arguments ' + names + ' is required';
|
|---|
| 598 | self.error(msg);
|
|---|
| 599 | }
|
|---|
| 600 | }
|
|---|
| 601 | });
|
|---|
| 602 |
|
|---|
| 603 | // return the updated namespace and the extra arguments
|
|---|
| 604 | return [ namespace, extras ];
|
|---|
| 605 | };
|
|---|
| 606 |
|
|---|
| 607 | ArgumentParser.prototype._readArgsFromFiles = function (argStrings) {
|
|---|
| 608 | // expand arguments referencing files
|
|---|
| 609 | var self = this;
|
|---|
| 610 | var fs = require('fs');
|
|---|
| 611 | var newArgStrings = [];
|
|---|
| 612 | argStrings.forEach(function (argString) {
|
|---|
| 613 | if (self.fromfilePrefixChars.indexOf(argString[0]) < 0) {
|
|---|
| 614 | // for regular arguments, just add them back into the list
|
|---|
| 615 | newArgStrings.push(argString);
|
|---|
| 616 | } else {
|
|---|
| 617 | // replace arguments referencing files with the file content
|
|---|
| 618 | try {
|
|---|
| 619 | var argstrs = [];
|
|---|
| 620 | var filename = argString.slice(1);
|
|---|
| 621 | var content = fs.readFileSync(filename, 'utf8');
|
|---|
| 622 | content = content.trim().split('\n');
|
|---|
| 623 | content.forEach(function (argLine) {
|
|---|
| 624 | self.convertArgLineToArgs(argLine).forEach(function (arg) {
|
|---|
| 625 | argstrs.push(arg);
|
|---|
| 626 | });
|
|---|
| 627 | argstrs = self._readArgsFromFiles(argstrs);
|
|---|
| 628 | });
|
|---|
| 629 | newArgStrings.push.apply(newArgStrings, argstrs);
|
|---|
| 630 | } catch (error) {
|
|---|
| 631 | return self.error(error.message);
|
|---|
| 632 | }
|
|---|
| 633 | }
|
|---|
| 634 | });
|
|---|
| 635 | return newArgStrings;
|
|---|
| 636 | };
|
|---|
| 637 |
|
|---|
| 638 | ArgumentParser.prototype.convertArgLineToArgs = function (argLine) {
|
|---|
| 639 | return [ argLine ];
|
|---|
| 640 | };
|
|---|
| 641 |
|
|---|
| 642 | ArgumentParser.prototype._matchArgument = function (action, regexpArgStrings) {
|
|---|
| 643 |
|
|---|
| 644 | // match the pattern for this action to the arg strings
|
|---|
| 645 | var regexpNargs = new RegExp('^' + this._getNargsPattern(action));
|
|---|
| 646 | var matches = regexpArgStrings.match(regexpNargs);
|
|---|
| 647 | var message;
|
|---|
| 648 |
|
|---|
| 649 | // throw an exception if we weren't able to find a match
|
|---|
| 650 | if (!matches) {
|
|---|
| 651 | switch (action.nargs) {
|
|---|
| 652 | /*eslint-disable no-undefined*/
|
|---|
| 653 | case undefined:
|
|---|
| 654 | case null:
|
|---|
| 655 | message = 'Expected one argument.';
|
|---|
| 656 | break;
|
|---|
| 657 | case c.OPTIONAL:
|
|---|
| 658 | message = 'Expected at most one argument.';
|
|---|
| 659 | break;
|
|---|
| 660 | case c.ONE_OR_MORE:
|
|---|
| 661 | message = 'Expected at least one argument.';
|
|---|
| 662 | break;
|
|---|
| 663 | default:
|
|---|
| 664 | message = 'Expected %s argument(s)';
|
|---|
| 665 | }
|
|---|
| 666 |
|
|---|
| 667 | throw argumentErrorHelper(
|
|---|
| 668 | action,
|
|---|
| 669 | format(message, action.nargs)
|
|---|
| 670 | );
|
|---|
| 671 | }
|
|---|
| 672 | // return the number of arguments matched
|
|---|
| 673 | return matches[1].length;
|
|---|
| 674 | };
|
|---|
| 675 |
|
|---|
| 676 | ArgumentParser.prototype._matchArgumentsPartial = function (actions, regexpArgStrings) {
|
|---|
| 677 | // progressively shorten the actions list by slicing off the
|
|---|
| 678 | // final actions until we find a match
|
|---|
| 679 | var self = this;
|
|---|
| 680 | var result = [];
|
|---|
| 681 | var actionSlice, pattern, matches;
|
|---|
| 682 | var i, j;
|
|---|
| 683 |
|
|---|
| 684 | function getLength(string) {
|
|---|
| 685 | return string.length;
|
|---|
| 686 | }
|
|---|
| 687 |
|
|---|
| 688 | for (i = actions.length; i > 0; i--) {
|
|---|
| 689 | pattern = '';
|
|---|
| 690 | actionSlice = actions.slice(0, i);
|
|---|
| 691 | for (j = 0; j < actionSlice.length; j++) {
|
|---|
| 692 | pattern += self._getNargsPattern(actionSlice[j]);
|
|---|
| 693 | }
|
|---|
| 694 |
|
|---|
| 695 | pattern = new RegExp('^' + pattern);
|
|---|
| 696 | matches = regexpArgStrings.match(pattern);
|
|---|
| 697 |
|
|---|
| 698 | if (matches && matches.length > 0) {
|
|---|
| 699 | // need only groups
|
|---|
| 700 | matches = matches.splice(1);
|
|---|
| 701 | result = result.concat(matches.map(getLength));
|
|---|
| 702 | break;
|
|---|
| 703 | }
|
|---|
| 704 | }
|
|---|
| 705 |
|
|---|
| 706 | // return the list of arg string counts
|
|---|
| 707 | return result;
|
|---|
| 708 | };
|
|---|
| 709 |
|
|---|
| 710 | ArgumentParser.prototype._parseOptional = function (argString) {
|
|---|
| 711 | var action, optionString, argExplicit, optionTuples;
|
|---|
| 712 |
|
|---|
| 713 | // if it's an empty string, it was meant to be a positional
|
|---|
| 714 | if (!argString) {
|
|---|
| 715 | return null;
|
|---|
| 716 | }
|
|---|
| 717 |
|
|---|
| 718 | // if it doesn't start with a prefix, it was meant to be positional
|
|---|
| 719 | if (this.prefixChars.indexOf(argString[0]) < 0) {
|
|---|
| 720 | return null;
|
|---|
| 721 | }
|
|---|
| 722 |
|
|---|
| 723 | // if the option string is present in the parser, return the action
|
|---|
| 724 | if (this._optionStringActions[argString]) {
|
|---|
| 725 | return [ this._optionStringActions[argString], argString, null ];
|
|---|
| 726 | }
|
|---|
| 727 |
|
|---|
| 728 | // if it's just a single character, it was meant to be positional
|
|---|
| 729 | if (argString.length === 1) {
|
|---|
| 730 | return null;
|
|---|
| 731 | }
|
|---|
| 732 |
|
|---|
| 733 | // if the option string before the "=" is present, return the action
|
|---|
| 734 | if (argString.indexOf('=') >= 0) {
|
|---|
| 735 | optionString = argString.split('=', 1)[0];
|
|---|
| 736 | argExplicit = argString.slice(optionString.length + 1);
|
|---|
| 737 |
|
|---|
| 738 | if (this._optionStringActions[optionString]) {
|
|---|
| 739 | action = this._optionStringActions[optionString];
|
|---|
| 740 | return [ action, optionString, argExplicit ];
|
|---|
| 741 | }
|
|---|
| 742 | }
|
|---|
| 743 |
|
|---|
| 744 | // search through all possible prefixes of the option string
|
|---|
| 745 | // and all actions in the parser for possible interpretations
|
|---|
| 746 | optionTuples = this._getOptionTuples(argString);
|
|---|
| 747 |
|
|---|
| 748 | // if multiple actions match, the option string was ambiguous
|
|---|
| 749 | if (optionTuples.length > 1) {
|
|---|
| 750 | var optionStrings = optionTuples.map(function (optionTuple) {
|
|---|
| 751 | return optionTuple[1];
|
|---|
| 752 | });
|
|---|
| 753 | this.error(format(
|
|---|
| 754 | 'Ambiguous option: "%s" could match %s.',
|
|---|
| 755 | argString, optionStrings.join(', ')
|
|---|
| 756 | ));
|
|---|
| 757 | // if exactly one action matched, this segmentation is good,
|
|---|
| 758 | // so return the parsed action
|
|---|
| 759 | } else if (optionTuples.length === 1) {
|
|---|
| 760 | return optionTuples[0];
|
|---|
| 761 | }
|
|---|
| 762 |
|
|---|
| 763 | // if it was not found as an option, but it looks like a negative
|
|---|
| 764 | // number, it was meant to be positional
|
|---|
| 765 | // unless there are negative-number-like options
|
|---|
| 766 | if (argString.match(this._regexpNegativeNumber)) {
|
|---|
| 767 | if (!this._hasNegativeNumberOptionals.some(Boolean)) {
|
|---|
| 768 | return null;
|
|---|
| 769 | }
|
|---|
| 770 | }
|
|---|
| 771 | // if it contains a space, it was meant to be a positional
|
|---|
| 772 | if (argString.search(' ') >= 0) {
|
|---|
| 773 | return null;
|
|---|
| 774 | }
|
|---|
| 775 |
|
|---|
| 776 | // it was meant to be an optional but there is no such option
|
|---|
| 777 | // in this parser (though it might be a valid option in a subparser)
|
|---|
| 778 | return [ null, argString, null ];
|
|---|
| 779 | };
|
|---|
| 780 |
|
|---|
| 781 | ArgumentParser.prototype._getOptionTuples = function (optionString) {
|
|---|
| 782 | var result = [];
|
|---|
| 783 | var chars = this.prefixChars;
|
|---|
| 784 | var optionPrefix;
|
|---|
| 785 | var argExplicit;
|
|---|
| 786 | var action;
|
|---|
| 787 | var actionOptionString;
|
|---|
| 788 |
|
|---|
| 789 | // option strings starting with two prefix characters are only split at
|
|---|
| 790 | // the '='
|
|---|
| 791 | if (chars.indexOf(optionString[0]) >= 0 && chars.indexOf(optionString[1]) >= 0) {
|
|---|
| 792 | if (optionString.indexOf('=') >= 0) {
|
|---|
| 793 | var optionStringSplit = optionString.split('=', 1);
|
|---|
| 794 |
|
|---|
| 795 | optionPrefix = optionStringSplit[0];
|
|---|
| 796 | argExplicit = optionStringSplit[1];
|
|---|
| 797 | } else {
|
|---|
| 798 | optionPrefix = optionString;
|
|---|
| 799 | argExplicit = null;
|
|---|
| 800 | }
|
|---|
| 801 |
|
|---|
| 802 | for (actionOptionString in this._optionStringActions) {
|
|---|
| 803 | if (actionOptionString.substr(0, optionPrefix.length) === optionPrefix) {
|
|---|
| 804 | action = this._optionStringActions[actionOptionString];
|
|---|
| 805 | result.push([ action, actionOptionString, argExplicit ]);
|
|---|
| 806 | }
|
|---|
| 807 | }
|
|---|
| 808 |
|
|---|
| 809 | // single character options can be concatenated with their arguments
|
|---|
| 810 | // but multiple character options always have to have their argument
|
|---|
| 811 | // separate
|
|---|
| 812 | } else if (chars.indexOf(optionString[0]) >= 0 && chars.indexOf(optionString[1]) < 0) {
|
|---|
| 813 | optionPrefix = optionString;
|
|---|
| 814 | argExplicit = null;
|
|---|
| 815 | var optionPrefixShort = optionString.substr(0, 2);
|
|---|
| 816 | var argExplicitShort = optionString.substr(2);
|
|---|
| 817 |
|
|---|
| 818 | for (actionOptionString in this._optionStringActions) {
|
|---|
| 819 | if (!$$.has(this._optionStringActions, actionOptionString)) continue;
|
|---|
| 820 |
|
|---|
| 821 | action = this._optionStringActions[actionOptionString];
|
|---|
| 822 | if (actionOptionString === optionPrefixShort) {
|
|---|
| 823 | result.push([ action, actionOptionString, argExplicitShort ]);
|
|---|
| 824 | } else if (actionOptionString.substr(0, optionPrefix.length) === optionPrefix) {
|
|---|
| 825 | result.push([ action, actionOptionString, argExplicit ]);
|
|---|
| 826 | }
|
|---|
| 827 | }
|
|---|
| 828 |
|
|---|
| 829 | // shouldn't ever get here
|
|---|
| 830 | } else {
|
|---|
| 831 | throw new Error(format('Unexpected option string: %s.', optionString));
|
|---|
| 832 | }
|
|---|
| 833 | // return the collected option tuples
|
|---|
| 834 | return result;
|
|---|
| 835 | };
|
|---|
| 836 |
|
|---|
| 837 | ArgumentParser.prototype._getNargsPattern = function (action) {
|
|---|
| 838 | // in all examples below, we have to allow for '--' args
|
|---|
| 839 | // which are represented as '-' in the pattern
|
|---|
| 840 | var regexpNargs;
|
|---|
| 841 |
|
|---|
| 842 | switch (action.nargs) {
|
|---|
| 843 | // the default (null) is assumed to be a single argument
|
|---|
| 844 | case undefined:
|
|---|
| 845 | case null:
|
|---|
| 846 | regexpNargs = '(-*A-*)';
|
|---|
| 847 | break;
|
|---|
| 848 | // allow zero or more arguments
|
|---|
| 849 | case c.OPTIONAL:
|
|---|
| 850 | regexpNargs = '(-*A?-*)';
|
|---|
| 851 | break;
|
|---|
| 852 | // allow zero or more arguments
|
|---|
| 853 | case c.ZERO_OR_MORE:
|
|---|
| 854 | regexpNargs = '(-*[A-]*)';
|
|---|
| 855 | break;
|
|---|
| 856 | // allow one or more arguments
|
|---|
| 857 | case c.ONE_OR_MORE:
|
|---|
| 858 | regexpNargs = '(-*A[A-]*)';
|
|---|
| 859 | break;
|
|---|
| 860 | // allow any number of options or arguments
|
|---|
| 861 | case c.REMAINDER:
|
|---|
| 862 | regexpNargs = '([-AO]*)';
|
|---|
| 863 | break;
|
|---|
| 864 | // allow one argument followed by any number of options or arguments
|
|---|
| 865 | case c.PARSER:
|
|---|
| 866 | regexpNargs = '(-*A[-AO]*)';
|
|---|
| 867 | break;
|
|---|
| 868 | // all others should be integers
|
|---|
| 869 | default:
|
|---|
| 870 | regexpNargs = '(-*' + $$.repeat('-*A', action.nargs) + '-*)';
|
|---|
| 871 | }
|
|---|
| 872 |
|
|---|
| 873 | // if this is an optional action, -- is not allowed
|
|---|
| 874 | if (action.isOptional()) {
|
|---|
| 875 | regexpNargs = regexpNargs.replace(/-\*/g, '');
|
|---|
| 876 | regexpNargs = regexpNargs.replace(/-/g, '');
|
|---|
| 877 | }
|
|---|
| 878 |
|
|---|
| 879 | // return the pattern
|
|---|
| 880 | return regexpNargs;
|
|---|
| 881 | };
|
|---|
| 882 |
|
|---|
| 883 | //
|
|---|
| 884 | // Value conversion methods
|
|---|
| 885 | //
|
|---|
| 886 |
|
|---|
| 887 | ArgumentParser.prototype._getValues = function (action, argStrings) {
|
|---|
| 888 | var self = this;
|
|---|
| 889 |
|
|---|
| 890 | // for everything but PARSER args, strip out '--'
|
|---|
| 891 | if (action.nargs !== c.PARSER && action.nargs !== c.REMAINDER) {
|
|---|
| 892 | argStrings = argStrings.filter(function (arrayElement) {
|
|---|
| 893 | return arrayElement !== '--';
|
|---|
| 894 | });
|
|---|
| 895 | }
|
|---|
| 896 |
|
|---|
| 897 | var value, argString;
|
|---|
| 898 |
|
|---|
| 899 | // optional argument produces a default when not present
|
|---|
| 900 | if (argStrings.length === 0 && action.nargs === c.OPTIONAL) {
|
|---|
| 901 |
|
|---|
| 902 | value = (action.isOptional()) ? action.constant : action.defaultValue;
|
|---|
| 903 |
|
|---|
| 904 | if (typeof (value) === 'string') {
|
|---|
| 905 | value = this._getValue(action, value);
|
|---|
| 906 | this._checkValue(action, value);
|
|---|
| 907 | }
|
|---|
| 908 |
|
|---|
| 909 | // when nargs='*' on a positional, if there were no command-line
|
|---|
| 910 | // args, use the default if it is anything other than None
|
|---|
| 911 | } else if (argStrings.length === 0 && action.nargs === c.ZERO_OR_MORE &&
|
|---|
| 912 | action.optionStrings.length === 0) {
|
|---|
| 913 |
|
|---|
| 914 | value = (action.defaultValue || argStrings);
|
|---|
| 915 | this._checkValue(action, value);
|
|---|
| 916 |
|
|---|
| 917 | // single argument or optional argument produces a single value
|
|---|
| 918 | } else if (argStrings.length === 1 &&
|
|---|
| 919 | (!action.nargs || action.nargs === c.OPTIONAL)) {
|
|---|
| 920 |
|
|---|
| 921 | argString = argStrings[0];
|
|---|
| 922 | value = this._getValue(action, argString);
|
|---|
| 923 | this._checkValue(action, value);
|
|---|
| 924 |
|
|---|
| 925 | // REMAINDER arguments convert all values, checking none
|
|---|
| 926 | } else if (action.nargs === c.REMAINDER) {
|
|---|
| 927 | value = argStrings.map(function (v) {
|
|---|
| 928 | return self._getValue(action, v);
|
|---|
| 929 | });
|
|---|
| 930 |
|
|---|
| 931 | // PARSER arguments convert all values, but check only the first
|
|---|
| 932 | } else if (action.nargs === c.PARSER) {
|
|---|
| 933 | value = argStrings.map(function (v) {
|
|---|
| 934 | return self._getValue(action, v);
|
|---|
| 935 | });
|
|---|
| 936 | this._checkValue(action, value[0]);
|
|---|
| 937 |
|
|---|
| 938 | // all other types of nargs produce a list
|
|---|
| 939 | } else {
|
|---|
| 940 | value = argStrings.map(function (v) {
|
|---|
| 941 | return self._getValue(action, v);
|
|---|
| 942 | });
|
|---|
| 943 | value.forEach(function (v) {
|
|---|
| 944 | self._checkValue(action, v);
|
|---|
| 945 | });
|
|---|
| 946 | }
|
|---|
| 947 |
|
|---|
| 948 | // return the converted value
|
|---|
| 949 | return value;
|
|---|
| 950 | };
|
|---|
| 951 |
|
|---|
| 952 | ArgumentParser.prototype._getValue = function (action, argString) {
|
|---|
| 953 | var result;
|
|---|
| 954 |
|
|---|
| 955 | var typeFunction = this._registryGet('type', action.type, action.type);
|
|---|
| 956 | if (typeof typeFunction !== 'function') {
|
|---|
| 957 | var message = format('%s is not callable', typeFunction);
|
|---|
| 958 | throw argumentErrorHelper(action, message);
|
|---|
| 959 | }
|
|---|
| 960 |
|
|---|
| 961 | // convert the value to the appropriate type
|
|---|
| 962 | try {
|
|---|
| 963 | result = typeFunction(argString);
|
|---|
| 964 |
|
|---|
| 965 | // ArgumentTypeErrors indicate errors
|
|---|
| 966 | // If action.type is not a registered string, it is a function
|
|---|
| 967 | // Try to deduce its name for inclusion in the error message
|
|---|
| 968 | // Failing that, include the error message it raised.
|
|---|
| 969 | } catch (e) {
|
|---|
| 970 | var name = null;
|
|---|
| 971 | if (typeof action.type === 'string') {
|
|---|
| 972 | name = action.type;
|
|---|
| 973 | } else {
|
|---|
| 974 | name = action.type.name || action.type.displayName || '<function>';
|
|---|
| 975 | }
|
|---|
| 976 | var msg = format('Invalid %s value: %s', name, argString);
|
|---|
| 977 | if (name === '<function>') { msg += '\n' + e.message; }
|
|---|
| 978 | throw argumentErrorHelper(action, msg);
|
|---|
| 979 | }
|
|---|
| 980 | // return the converted value
|
|---|
| 981 | return result;
|
|---|
| 982 | };
|
|---|
| 983 |
|
|---|
| 984 | ArgumentParser.prototype._checkValue = function (action, value) {
|
|---|
| 985 | // converted value must be one of the choices (if specified)
|
|---|
| 986 | var choices = action.choices;
|
|---|
| 987 | if (choices) {
|
|---|
| 988 | // choise for argument can by array or string
|
|---|
| 989 | if ((typeof choices === 'string' || Array.isArray(choices)) &&
|
|---|
| 990 | choices.indexOf(value) !== -1) {
|
|---|
| 991 | return;
|
|---|
| 992 | }
|
|---|
| 993 | // choise for subparsers can by only hash
|
|---|
| 994 | if (typeof choices === 'object' && !Array.isArray(choices) && choices[value]) {
|
|---|
| 995 | return;
|
|---|
| 996 | }
|
|---|
| 997 |
|
|---|
| 998 | if (typeof choices === 'string') {
|
|---|
| 999 | choices = choices.split('').join(', ');
|
|---|
| 1000 | } else if (Array.isArray(choices)) {
|
|---|
| 1001 | choices = choices.join(', ');
|
|---|
| 1002 | } else {
|
|---|
| 1003 | choices = Object.keys(choices).join(', ');
|
|---|
| 1004 | }
|
|---|
| 1005 | var message = format('Invalid choice: %s (choose from [%s])', value, choices);
|
|---|
| 1006 | throw argumentErrorHelper(action, message);
|
|---|
| 1007 | }
|
|---|
| 1008 | };
|
|---|
| 1009 |
|
|---|
| 1010 | //
|
|---|
| 1011 | // Help formatting methods
|
|---|
| 1012 | //
|
|---|
| 1013 |
|
|---|
| 1014 | /**
|
|---|
| 1015 | * ArgumentParser#formatUsage -> string
|
|---|
| 1016 | *
|
|---|
| 1017 | * Return usage string
|
|---|
| 1018 | *
|
|---|
| 1019 | * See also [original guide][1]
|
|---|
| 1020 | *
|
|---|
| 1021 | * [1]:http://docs.python.org/dev/library/argparse.html#printing-help
|
|---|
| 1022 | **/
|
|---|
| 1023 | ArgumentParser.prototype.formatUsage = function () {
|
|---|
| 1024 | var formatter = this._getFormatter();
|
|---|
| 1025 | formatter.addUsage(this.usage, this._actions, this._mutuallyExclusiveGroups);
|
|---|
| 1026 | return formatter.formatHelp();
|
|---|
| 1027 | };
|
|---|
| 1028 |
|
|---|
| 1029 | /**
|
|---|
| 1030 | * ArgumentParser#formatHelp -> string
|
|---|
| 1031 | *
|
|---|
| 1032 | * Return help
|
|---|
| 1033 | *
|
|---|
| 1034 | * See also [original guide][1]
|
|---|
| 1035 | *
|
|---|
| 1036 | * [1]:http://docs.python.org/dev/library/argparse.html#printing-help
|
|---|
| 1037 | **/
|
|---|
| 1038 | ArgumentParser.prototype.formatHelp = function () {
|
|---|
| 1039 | var formatter = this._getFormatter();
|
|---|
| 1040 |
|
|---|
| 1041 | // usage
|
|---|
| 1042 | formatter.addUsage(this.usage, this._actions, this._mutuallyExclusiveGroups);
|
|---|
| 1043 |
|
|---|
| 1044 | // description
|
|---|
| 1045 | formatter.addText(this.description);
|
|---|
| 1046 |
|
|---|
| 1047 | // positionals, optionals and user-defined groups
|
|---|
| 1048 | this._actionGroups.forEach(function (actionGroup) {
|
|---|
| 1049 | formatter.startSection(actionGroup.title);
|
|---|
| 1050 | formatter.addText(actionGroup.description);
|
|---|
| 1051 | formatter.addArguments(actionGroup._groupActions);
|
|---|
| 1052 | formatter.endSection();
|
|---|
| 1053 | });
|
|---|
| 1054 |
|
|---|
| 1055 | // epilog
|
|---|
| 1056 | formatter.addText(this.epilog);
|
|---|
| 1057 |
|
|---|
| 1058 | // determine help from format above
|
|---|
| 1059 | return formatter.formatHelp();
|
|---|
| 1060 | };
|
|---|
| 1061 |
|
|---|
| 1062 | ArgumentParser.prototype._getFormatter = function () {
|
|---|
| 1063 | var FormatterClass = this.formatterClass;
|
|---|
| 1064 | var formatter = new FormatterClass({ prog: this.prog });
|
|---|
| 1065 | return formatter;
|
|---|
| 1066 | };
|
|---|
| 1067 |
|
|---|
| 1068 | //
|
|---|
| 1069 | // Print functions
|
|---|
| 1070 | //
|
|---|
| 1071 |
|
|---|
| 1072 | /**
|
|---|
| 1073 | * ArgumentParser#printUsage() -> Void
|
|---|
| 1074 | *
|
|---|
| 1075 | * Print usage
|
|---|
| 1076 | *
|
|---|
| 1077 | * See also [original guide][1]
|
|---|
| 1078 | *
|
|---|
| 1079 | * [1]:http://docs.python.org/dev/library/argparse.html#printing-help
|
|---|
| 1080 | **/
|
|---|
| 1081 | ArgumentParser.prototype.printUsage = function () {
|
|---|
| 1082 | this._printMessage(this.formatUsage());
|
|---|
| 1083 | };
|
|---|
| 1084 |
|
|---|
| 1085 | /**
|
|---|
| 1086 | * ArgumentParser#printHelp() -> Void
|
|---|
| 1087 | *
|
|---|
| 1088 | * Print help
|
|---|
| 1089 | *
|
|---|
| 1090 | * See also [original guide][1]
|
|---|
| 1091 | *
|
|---|
| 1092 | * [1]:http://docs.python.org/dev/library/argparse.html#printing-help
|
|---|
| 1093 | **/
|
|---|
| 1094 | ArgumentParser.prototype.printHelp = function () {
|
|---|
| 1095 | this._printMessage(this.formatHelp());
|
|---|
| 1096 | };
|
|---|
| 1097 |
|
|---|
| 1098 | ArgumentParser.prototype._printMessage = function (message, stream) {
|
|---|
| 1099 | if (!stream) {
|
|---|
| 1100 | stream = process.stdout;
|
|---|
| 1101 | }
|
|---|
| 1102 | if (message) {
|
|---|
| 1103 | stream.write('' + message);
|
|---|
| 1104 | }
|
|---|
| 1105 | };
|
|---|
| 1106 |
|
|---|
| 1107 | //
|
|---|
| 1108 | // Exit functions
|
|---|
| 1109 | //
|
|---|
| 1110 |
|
|---|
| 1111 | /**
|
|---|
| 1112 | * ArgumentParser#exit(status=0, message) -> Void
|
|---|
| 1113 | * - status (int): exit status
|
|---|
| 1114 | * - message (string): message
|
|---|
| 1115 | *
|
|---|
| 1116 | * Print message in stderr/stdout and exit program
|
|---|
| 1117 | **/
|
|---|
| 1118 | ArgumentParser.prototype.exit = function (status, message) {
|
|---|
| 1119 | if (message) {
|
|---|
| 1120 | if (status === 0) {
|
|---|
| 1121 | this._printMessage(message);
|
|---|
| 1122 | } else {
|
|---|
| 1123 | this._printMessage(message, process.stderr);
|
|---|
| 1124 | }
|
|---|
| 1125 | }
|
|---|
| 1126 |
|
|---|
| 1127 | process.exit(status);
|
|---|
| 1128 | };
|
|---|
| 1129 |
|
|---|
| 1130 | /**
|
|---|
| 1131 | * ArgumentParser#error(message) -> Void
|
|---|
| 1132 | * - err (Error|string): message
|
|---|
| 1133 | *
|
|---|
| 1134 | * Error method Prints a usage message incorporating the message to stderr and
|
|---|
| 1135 | * exits. If you override this in a subclass,
|
|---|
| 1136 | * it should not return -- it should
|
|---|
| 1137 | * either exit or throw an exception.
|
|---|
| 1138 | *
|
|---|
| 1139 | **/
|
|---|
| 1140 | ArgumentParser.prototype.error = function (err) {
|
|---|
| 1141 | var message;
|
|---|
| 1142 | if (err instanceof Error) {
|
|---|
| 1143 | if (this.debug === true) {
|
|---|
| 1144 | throw err;
|
|---|
| 1145 | }
|
|---|
| 1146 | message = err.message;
|
|---|
| 1147 | } else {
|
|---|
| 1148 | message = err;
|
|---|
| 1149 | }
|
|---|
| 1150 | var msg = format('%s: error: %s', this.prog, message) + c.EOL;
|
|---|
| 1151 |
|
|---|
| 1152 | if (this.debug === true) {
|
|---|
| 1153 | throw new Error(msg);
|
|---|
| 1154 | }
|
|---|
| 1155 |
|
|---|
| 1156 | this.printUsage(process.stderr);
|
|---|
| 1157 |
|
|---|
| 1158 | return this.exit(2, msg);
|
|---|
| 1159 | };
|
|---|
| 1160 |
|
|---|
| 1161 | module.exports = ArgumentParser;
|
|---|