1161 lines · 34.4 KB · JavaScript
RawDownload
/** * class ArgumentParser * * Object for parsing command line strings into js objects. * * Inherited from [[ActionContainer]] **/'use strict'; var util    = require('util');var format  = require('util').format;var Path    = require('path');var sprintf = require('sprintf-js').sprintf; // Constantsvar c = require('./const'); var $$ = require('./utils'); var ActionContainer = require('./action_container'); // Errorsvar argumentErrorHelper = require('./argument/error'); var HelpFormatter = require('./help/formatter'); var Namespace = require('./namespace');  /** * new ArgumentParser(options) * * Create a new ArgumentParser object. * * ##### Options: * - `prog`  The name of the program (default: Path.basename(process.argv[1])) * - `usage`  A usage message (default: auto-generated from arguments) * - `description`  A description of what the program does * - `epilog`  Text following the argument descriptions * - `parents`  Parsers whose arguments should be copied into this one * - `formatterClass`  HelpFormatter class for printing help messages * - `prefixChars`  Characters that prefix optional arguments * - `fromfilePrefixChars` Characters that prefix files containing additional arguments * - `argumentDefault`  The default value for all arguments * - `addHelp`  Add a -h/-help option * - `conflictHandler`  Specifies how to handle conflicting argument names * - `debug`  Enable debug mode. Argument errors throw exception in *   debug mode and process.exit in normal. Used for development and *   testing (default: false) * * See also [original guide][1] * * [1]:http://docs.python.org/dev/library/argparse.html#argumentparser-objects **/function ArgumentParser(options) {  if (!(this instanceof ArgumentParser)) {    return new ArgumentParser(options);  }  var self = this;  options = options || {};   options.description = (options.description || null);  options.argumentDefault = (options.argumentDefault || null);  options.prefixChars = (options.prefixChars || '-');  options.conflictHandler = (options.conflictHandler || 'error');  ActionContainer.call(this, options);   options.addHelp = typeof options.addHelp === 'undefined' || !!options.addHelp;  options.parents = options.parents || [];  // default program name  options.prog = (options.prog || Path.basename(process.argv[1]));  this.prog = options.prog;  this.usage = options.usage;  this.epilog = options.epilog;  this.version = options.version;   this.debug = (options.debug === true);   this.formatterClass = (options.formatterClass || HelpFormatter);  this.fromfilePrefixChars = options.fromfilePrefixChars || null;  this._positionals = this.addArgumentGroup({ title: 'Positional arguments' });  this._optionals = this.addArgumentGroup({ title: 'Optional arguments' });  this._subparsers = null;   // register types  function FUNCTION_IDENTITY(o) {    return o;  }  this.register('type', 'auto', FUNCTION_IDENTITY);  this.register('type', null, FUNCTION_IDENTITY);  this.register('type', 'int', function (x) {    var result = parseInt(x, 10);    if (isNaN(result)) {      throw new Error(x + ' is not a valid integer.');    }    return result;  });  this.register('type', 'float', function (x) {    var result = parseFloat(x);    if (isNaN(result)) {      throw new Error(x + ' is not a valid float.');    }    return result;  });  this.register('type', 'string', function (x) {    return '' + x;  });   // add help and version arguments if necessary  var defaultPrefix = (this.prefixChars.indexOf('-') > -1) ? '-' : this.prefixChars[0];  if (options.addHelp) {    this.addArgument(      [ defaultPrefix + 'h', defaultPrefix + defaultPrefix + 'help' ],      {        action: 'help',        defaultValue: c.SUPPRESS,        help: 'Show this help message and exit.'      }    );  }  if (typeof this.version !== 'undefined') {    this.addArgument(      [ defaultPrefix + 'v', defaultPrefix + defaultPrefix + 'version' ],      {        action: 'version',        version: this.version,        defaultValue: c.SUPPRESS,        help: "Show program's version number and exit."      }    );  }   // add parent arguments and defaults  options.parents.forEach(function (parent) {    self._addContainerActions(parent);    if (typeof parent._defaults !== 'undefined') {      for (var defaultKey in parent._defaults) {        if (parent._defaults.hasOwnProperty(defaultKey)) {          self._defaults[defaultKey] = parent._defaults[defaultKey];        }      }    }  });} util.inherits(ArgumentParser, ActionContainer); /** * ArgumentParser#addSubparsers(options) -> [[ActionSubparsers]] * - options (object): hash of options see [[ActionSubparsers.new]] * * See also [subcommands][1] * * [1]:http://docs.python.org/dev/library/argparse.html#sub-commands **/ArgumentParser.prototype.addSubparsers = function (options) {  if (this._subparsers) {    this.error('Cannot have multiple subparser arguments.');  }   options = options || {};  options.debug = (this.debug === true);  options.optionStrings = [];  options.parserClass = (options.parserClass || ArgumentParser);    if (!!options.title || !!options.description) {     this._subparsers = this.addArgumentGroup({      title: (options.title || 'subcommands'),      description: options.description    });    delete options.title;    delete options.description;   } else {    this._subparsers = this._positionals;  }   // prog defaults to the usage message of this parser, skipping  // optional arguments and with no "usage:" prefix  if (!options.prog) {    var formatter = this._getFormatter();    var positionals = this._getPositionalActions();    var groups = this._mutuallyExclusiveGroups;    formatter.addUsage(this.usage, positionals, groups, '');    options.prog = formatter.formatHelp().trim();  }   // create the parsers action and add it to the positionals list  var ParsersClass = this._popActionClass(options, 'parsers');  var action = new ParsersClass(options);  this._subparsers._addAction(action);   // return the created parsers action  return action;}; ArgumentParser.prototype._addAction = function (action) {  if (action.isOptional()) {    this._optionals._addAction(action);  } else {    this._positionals._addAction(action);  }  return action;}; ArgumentParser.prototype._getOptionalActions = function () {  return this._actions.filter(function (action) {    return action.isOptional();  });}; ArgumentParser.prototype._getPositionalActions = function () {  return this._actions.filter(function (action) {    return action.isPositional();  });};  /** * ArgumentParser#parseArgs(args, namespace) -> Namespace|Object * - args (array): input elements * - namespace (Namespace|Object): result object * * Parsed args and throws error if some arguments are not recognized * * See also [original guide][1] * * [1]:http://docs.python.org/dev/library/argparse.html#the-parse-args-method **/ArgumentParser.prototype.parseArgs = function (args, namespace) {  var argv;  var result = this.parseKnownArgs(args, namespace);   args = result[0];  argv = result[1];  if (argv && argv.length > 0) {    this.error(      format('Unrecognized arguments: %s.', argv.join(' '))    );  }  return args;}; /** * ArgumentParser#parseKnownArgs(args, namespace) -> array * - args (array): input options * - namespace (Namespace|Object): result object * * Parse known arguments and return tuple of result object * and unknown args * * See also [original guide][1] * * [1]:http://docs.python.org/dev/library/argparse.html#partial-parsing **/ArgumentParser.prototype.parseKnownArgs = function (args, namespace) {  var self = this;   // args default to the system args  args = args || process.argv.slice(2);   // default Namespace built from parser defaults  namespace = namespace || new Namespace();   self._actions.forEach(function (action) {    if (action.dest !== c.SUPPRESS) {      if (!$$.has(namespace, action.dest)) {        if (action.defaultValue !== c.SUPPRESS) {          var defaultValue = action.defaultValue;          if (typeof action.defaultValue === 'string') {            defaultValue = self._getValue(action, defaultValue);          }          namespace[action.dest] = defaultValue;        }      }    }  });   Object.keys(self._defaults).forEach(function (dest) {    namespace[dest] = self._defaults[dest];  });   // parse the arguments and exit if there are any errors  try {    var res = this._parseKnownArgs(args, namespace);     namespace = res[0];    args = res[1];    if ($$.has(namespace, c._UNRECOGNIZED_ARGS_ATTR)) {      args = $$.arrayUnion(args, namespace[c._UNRECOGNIZED_ARGS_ATTR]);      delete namespace[c._UNRECOGNIZED_ARGS_ATTR];    }    return [ namespace, args ];  } catch (e) {    this.error(e);  }}; ArgumentParser.prototype._parseKnownArgs = function (argStrings, namespace) {  var self = this;   var extras = [];   // replace arg strings that are file references  if (this.fromfilePrefixChars !== null) {    argStrings = this._readArgsFromFiles(argStrings);  }  // map all mutually exclusive arguments to the other arguments  // they can't occur with  // Python has 'conflicts = action_conflicts.setdefault(mutex_action, [])'  // though I can't conceive of a way in which an action could be a member  // of two different mutually exclusive groups.   function actionHash(action) {    // some sort of hashable key for this action    // action itself cannot be a key in actionConflicts    // I think getName() (join of optionStrings) is unique enough    return action.getName();  }   var conflicts, key;  var actionConflicts = {};   this._mutuallyExclusiveGroups.forEach(function (mutexGroup) {    mutexGroup._groupActions.forEach(function (mutexAction, i, groupActions) {      key = actionHash(mutexAction);      if (!$$.has(actionConflicts, key)) {        actionConflicts[key] = [];      }      conflicts = actionConflicts[key];      conflicts.push.apply(conflicts, groupActions.slice(0, i));      conflicts.push.apply(conflicts, groupActions.slice(i + 1));    });  });   // find all option indices, and determine the arg_string_pattern  // which has an 'O' if there is an option at an index,  // an 'A' if there is an argument, or a '-' if there is a '--'  var optionStringIndices = {};   var argStringPatternParts = [];   argStrings.forEach(function (argString, argStringIndex) {    if (argString === '--') {      argStringPatternParts.push('-');      while (argStringIndex < argStrings.length) {        argStringPatternParts.push('A');        argStringIndex++;      }    } else {      // otherwise, add the arg to the arg strings      // and note the index if it was an option      var pattern;      var optionTuple = self._parseOptional(argString);      if (!optionTuple) {        pattern = 'A';      } else {        optionStringIndices[argStringIndex] = optionTuple;        pattern = 'O';      }      argStringPatternParts.push(pattern);    }  });  var argStringsPattern = argStringPatternParts.join('');   var seenActions = [];  var seenNonDefaultActions = [];    function takeAction(action, argumentStrings, optionString) {    seenActions.push(action);    var argumentValues = self._getValues(action, argumentStrings);     // error if this argument is not allowed with other previously    // seen arguments, assuming that actions that use the default    // value don't really count as "present"    if (argumentValues !== action.defaultValue) {      seenNonDefaultActions.push(action);      if (actionConflicts[actionHash(action)]) {        actionConflicts[actionHash(action)].forEach(function (actionConflict) {          if (seenNonDefaultActions.indexOf(actionConflict) >= 0) {            throw argumentErrorHelper(              action,              format('Not allowed with argument "%s".', actionConflict.getName())            );          }        });      }    }     if (argumentValues !== c.SUPPRESS) {      action.call(self, namespace, argumentValues, optionString);    }  }   function consumeOptional(startIndex) {    // get the optional identified at this index    var optionTuple = optionStringIndices[startIndex];    var action = optionTuple[0];    var optionString = optionTuple[1];    var explicitArg = optionTuple[2];     // identify additional optionals in the same arg string    // (e.g. -xyz is the same as -x -y -z if no args are required)    var actionTuples = [];     var args, argCount, start, stop;     for (;;) {      if (!action) {        extras.push(argStrings[startIndex]);        return startIndex + 1;      }      if (explicitArg) {        argCount = self._matchArgument(action, 'A');         // if the action is a single-dash option and takes no        // arguments, try to parse more single-dash options out        // of the tail of the option string        var chars = self.prefixChars;        if (argCount === 0 && chars.indexOf(optionString[1]) < 0) {          actionTuples.push([ action, [], optionString ]);          optionString = optionString[0] + explicitArg[0];          var newExplicitArg = explicitArg.slice(1) || null;          var optionalsMap = self._optionStringActions;           if (Object.keys(optionalsMap).indexOf(optionString) >= 0) {            action = optionalsMap[optionString];            explicitArg = newExplicitArg;          } else {            throw argumentErrorHelper(action, sprintf('ignored explicit argument %r', explicitArg));          }        } else if (argCount === 1) {          // if the action expect exactly one argument, we've          // successfully matched the option; exit the loop          stop = startIndex + 1;          args = [ explicitArg ];          actionTuples.push([ action, args, optionString ]);          break;        } else {          // error if a double-dash option did not use the          // explicit argument          throw argumentErrorHelper(action, sprintf('ignored explicit argument %r', explicitArg));        }      } else {        // if there is no explicit argument, try to match the        // optional's string arguments with the following strings        // if successful, exit the loop         start = startIndex + 1;        var selectedPatterns = argStringsPattern.substr(start);         argCount = self._matchArgument(action, selectedPatterns);        stop = start + argCount;          args = argStrings.slice(start, stop);         actionTuples.push([ action, args, optionString ]);        break;      }     }     // add the Optional to the list and return the index at which    // the Optional's string args stopped    if (actionTuples.length < 1) {      throw new Error('length should be > 0');    }    for (var i = 0; i < actionTuples.length; i++) {      takeAction.apply(self, actionTuples[i]);    }    return stop;  }   // the list of Positionals left to be parsed; this is modified  // by consume_positionals()  var positionals = self._getPositionalActions();   function consumePositionals(startIndex) {    // match as many Positionals as possible    var selectedPattern = argStringsPattern.substr(startIndex);    var argCounts = self._matchArgumentsPartial(positionals, selectedPattern);     // slice off the appropriate arg strings for each Positional    // and add the Positional and its args to the list    for (var i = 0; i < positionals.length; i++) {      var action = positionals[i];      var argCount = argCounts[i];      if (typeof argCount === 'undefined') {        continue;      }      var args = argStrings.slice(startIndex, startIndex + argCount);       startIndex += argCount;      takeAction(action, args);    } 

Lines 1–500 of 1161.