Skip to content

Commit

Permalink
Merge pull request #41 from hpaulj/exclusiveUsage
Browse files Browse the repository at this point in the history
Exclusive usage
  • Loading branch information
Vitaly Puzrin committed Jan 8, 2013
2 parents c552034 + e2fab72 commit a3f6aaf
Show file tree
Hide file tree
Showing 3 changed files with 93 additions and 16 deletions.
2 changes: 1 addition & 1 deletion lib/argument_parser.js
Expand Up @@ -370,7 +370,7 @@ ArgumentParser.prototype._parseKnownArgs = function (argStrings, namespace) {
// 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.default) {
if (argumentValues !== action.defaultValue) {
seenNonDefaultActions.push(action);
if (!!actionConflicts[actionHash(action)]) {
actionConflicts[actionHash(action)].forEach(function (actionConflict) {
Expand Down
30 changes: 15 additions & 15 deletions lib/help/formatter.js
Expand Up @@ -445,7 +445,8 @@ HelpFormatter.prototype._formatActionsUsage = function (actions, groups) {
if (start >= 0) {
end = start + group._groupActions.length;

if (actions.slice(start, end) === group._groupActions) {
//if (actions.slice(start, end) === group._groupActions) {
if (_.isEqual(actions.slice(start, end), group._groupActions)) {
group._groupActions.forEach(function (action) {
groupActions.push(action);
});
Expand Down Expand Up @@ -530,25 +531,24 @@ HelpFormatter.prototype._formatActionsUsage = function (actions, groups) {
});

// insert things at the necessary indices
inserts.reverse().forEach(function (insert, insertIndex) {
parts = parts.slice(0, insertIndex).concat(
[insert],
parts.slice(insertIndex + 1, parts.length - 1)
);
});
for (var i = inserts.length - 1; i >= 0; --i) {
if (inserts[i] !== null) {
parts.splice(i, 0, inserts[i]);
}
}

// join all the action items with spaces
var text = parts.filter(function (part) {
return !!part;
}).join(' ');

// clean up separators for mutually exclusive groups
var regexpOpen = '[\\[(]';
var regexpClose = '[\\])]';
text = text.replace('(' + regexpOpen + ') ', '\\1');
text = text.replace(' (' + regexpClose + ')', '\\1');
text = text.replace(regexpOpen + ' *' + regexpClose, '');
text = text.replace('\\(([^|]*)\\)', '\\1');
text = text.replace(/([\[(]) /g, '$1'); // remove spaces
text = text.replace(/ ([\])])/g, '$1');
text = text.replace(/\[ *\]/g, ''); // remove empty groups
text = text.replace(/\( *\)/g, '');
text = text.replace(/\(([^|]*)\)/g, '$1'); // remove () from single action groups

text = _.str.strip(text);

// return the text
Expand Down Expand Up @@ -701,11 +701,11 @@ HelpFormatter.prototype._formatArgs = function (action, metavarDefault) {
break;
case $$.ZERO_OR_MORE:
metavars = buildMetavar(2);
result = '[' + metavars[0] + '[' + metavars[1] + ' ...]]';
result = '[' + metavars[0] + ' [' + metavars[1] + ' ...]]';
break;
case $$.ONE_OR_MORE:
metavars = buildMetavar(2);
result = '' + metavars[0] + '[' + metavars[1] + ' ...]';
result = '' + metavars[0] + ' [' + metavars[1] + ' ...]';
break;
case $$.REMAINDER:
result = '...';
Expand Down
77 changes: 77 additions & 0 deletions test/testgroup.js
Expand Up @@ -102,6 +102,83 @@ describe('ArgumentParser', function () {
/one of the arguments (.*) is required/i
);
});
it('mutually exclusive group usage', function () {
// adapted from test_argparse.py TestMutuallyExclusiveSimple
var usage;
parser = new ArgumentParser({prog: 'PROG', debug: true});
group = parser.addMutuallyExclusiveGroup({required: true});
// or should the input be {required: true}?
group.addArgument(['--bar'], {help: 'bar help'});
group.addArgument(['--baz'], {nargs: '?', constant: 'Z', help: 'baz help'});
args = parser.parseArgs(['--bar', 'X']);
assert.deepEqual(args, {bar: 'X', baz: null});

assert.throws(
function () {
args = parser.parseArgs('--bar X --baz Y'.split(' '));
},
/Not allowed with argument/i
);
usage = parser.formatUsage();
assert.equal(usage, 'usage: PROG [-h] (--bar BAR | --baz [BAZ])\n');
group.required = false;
usage = parser.formatUsage();
assert.equal(usage, 'usage: PROG [-h] [--bar BAR | --baz [BAZ]]\n');
// could also test all or part of parser.formatHelp()
});
it('mutually exclusive optional and positional', function () {
// adapted from test_argparse.py TestMutuallyExclusiveOptionalAndPositional
var usage;
parser = new ArgumentParser({prog: 'PROG', debug: true});
group = parser.addMutuallyExclusiveGroup({required: true});
// or should the input be {required: true}?
group.addArgument(['--foo'], {action: 'storeTrue', help: 'foo help'});
group.addArgument(['--spam'], {help: 'spam help'});
group.addArgument(['badger'], {nargs: '*', defaultValue: 'X', help: 'badger help'});
args = parser.parseArgs(['--spam', 'S']);
assert.deepEqual(args, {foo: false, spam: 'S', badger: 'X'});
args = parser.parseArgs(['X']);
assert.deepEqual(args, {"foo": false, "spam": null, "badger": ['X']});
args = parser.parseArgs(['--foo']);
assert.deepEqual(args, {foo: true, spam: null, badger: 'X'});
assert.throws(
function () {
args = parser.parseArgs('--foo --spam 5'.split(' '));
},
/Not allowed with argument/i
);
usage = parser.formatUsage();
assert.equal(usage, 'usage: PROG [-h] (--foo | --spam SPAM | badger [badger ...])\n');
group.required = false;
usage = parser.formatUsage();
assert.equal(usage, 'usage: PROG [-h] [--foo | --spam SPAM | badger [badger ...]]\n');
});
it('two mutually exclusive groups', function () {
// adapted from test_argparse.py
var usage, group1, group2;
parser = new ArgumentParser({prog: 'PROG', debug: true});
group1 = parser.addMutuallyExclusiveGroup({required: true});
group1.addArgument(['--foo'], {action: 'storeTrue'});
group1.addArgument(['--bar'], {action: 'storeFalse'});
group2 = parser.addMutuallyExclusiveGroup({required: false});
group2.addArgument(['--soup'], {action: 'storeTrue'});
group2.addArgument(['--nuts'], {action: 'storeFalse'});
usage = parser.formatUsage();
assert.equal(usage, 'usage: PROG [-h] (--foo | --bar) [--soup | --nuts]\n');
});
it('suppressed and single action groups', function () {
// adapted from test_argparse.py
var usage, group1, group2;
parser = new ArgumentParser({prog: 'PROG', debug: true});
group1 = parser.addMutuallyExclusiveGroup();
group1.addArgument(['--sup'], {help: '==SUPPRESS=='});
// should produce an empty group (), which is removed
group2 = parser.addMutuallyExclusiveGroup({required: true});
group2.addArgument(['--xxx'], {});
// single entry in a required group, remove group ()
usage = parser.formatUsage();
assert.equal(usage, 'usage: PROG [-h] --xxx XXX\n');
});
});
});

0 comments on commit a3f6aaf

Please sign in to comment.