Permalink
Browse files

fromFile implementation

  • Loading branch information...
1 parent 5db8a2a commit ab7465bc7986bebff5c65eb607b7ef7bf63738ae @hpaulj hpaulj committed with ixti Jan 24, 2013
Showing with 162 additions and 3 deletions.
  1. +40 −3 lib/argument_parser.js
  2. +122 −0 test/fromfile.js
View
@@ -39,6 +39,7 @@ var Namespace = require('./namespace');
* - `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
@@ -72,7 +73,7 @@ var ArgumentParser = module.exports = function ArgumentParser(options) {
this.conflictHandler = options.conflictHandler;
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;
@@ -301,8 +302,9 @@ ArgumentParser.prototype._parseKnownArgs = function (argStrings, namespace) {
var extras = [];
// replace arg strings that are file references
- // ToDo read args from files
-
+ 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, [])'
@@ -610,6 +612,41 @@ ArgumentParser.prototype._parseKnownArgs = function (argStrings, namespace) {
return [namespace, extras];
};
+ArgumentParser.prototype._readArgsFromFiles = function (argStrings) {
+ // expand arguments referencing files
+ var _this = this;
+ var fs = require('fs');
+ var newArgStrings = [];
+ argStrings.forEach(function (argString) {
+ if (_this.fromfilePrefixChars.indexOf(argString[0]) < 0) {
+ // for regular arguments, just add them back into the list
+ newArgStrings.push(argString);
+ } else {
+ // replace arguments referencing files with the file content
+ try {
+ var argstrs = [];
+ var filename = argString.slice(1);
+ var content = fs.readFileSync(filename, 'utf8');
+ content = content.trim().split('\n');
+ content.forEach(function (argLine) {
+ _this.convertArgLineToArgs(argLine).forEach(function (arg) {
+ argstrs.push(arg);
+ });
+ argstrs = _this._readArgsFromFiles(argstrs);
+ });
+ newArgStrings.push.apply(newArgStrings, argstrs);
+ } catch (error) {
+ return _this.error(error.message);
+ }
+ }
+ });
+ return newArgStrings;
+};
+
+ArgumentParser.prototype.convertArgLineToArgs = function (argLine) {
+ return [argLine];
+};
+
ArgumentParser.prototype._matchArgument = function (action, regexpArgStrings) {
// match the pattern for this action to the arg strings
View
@@ -0,0 +1,122 @@
+/*global describe, it, beforeEach, before, after*/
+
+'use strict';
+
+var assert = require('assert');
+
+var ArgumentParser = require('../lib/argparse').ArgumentParser;
+
+var fs = require('fs');
+var os = require('os');
+var path = require('path');
+var assert = require('assert');
+var _ = require('underscore');
+_.str = require('underscore.string');
+
+var oldcwd = process.cwd();
+
+function setup_tempdir() {
+ // setup a temporary directory as cwd
+ var tdir = path.join(os.tmpDir(), 'argparse_temp');
+ try {
+ fs.mkdirSync(tdir);
+ } catch (error) {
+ if (!error.message.match(/EEXIST/)) {
+ throw error;
+ }
+ }
+ oldcwd = process.cwd();
+ process.chdir(tdir);
+ // console.log('Now in ' + process.cwd());
+ return oldcwd;
+}
+
+function teardown_tempdir(oldcwd) {
+ // remove the temp dir
+ var tdir = process.cwd();
+ process.chdir(oldcwd);
+ if (_.str.startsWith(tdir, os.tmpDir())) {
+ var dirls = fs.readdirSync(tdir);
+ //console.log(tdir, dirls)
+ dirls.forEach(function (f) {
+ fs.unlinkSync(path.join(tdir, f));
+ });
+ fs.rmdirSync(tdir);
+ //console.log('Removed ' + tdir);
+ }
+}
+
+function setup_files() {
+ var file_texts = [['hello', 'hello world!\n'],
+ ['recursive', '-a\n', 'A\n', '@hello'],
+ ['invalid', '@no-such-path\n']];
+
+ oldcwd = setup_tempdir();
+ // write the test files
+ file_texts.forEach(function (tpl) {
+ var filename = tpl[0];
+ var data = tpl.slice(1).join('');
+ fs.writeFileSync(filename, data);
+ });
+}
+
+describe('fromfilePrefixchars', function () {
+ var parser;
+ var args;
+ before(function () {
+ setup_files();
+ });
+ beforeEach(function () {
+ parser = new ArgumentParser({debug: true, fromfilePrefixChars: '@'});
+ parser.addArgument(['-a']);
+ parser.addArgument(['x']);
+ parser.addArgument(['y'], {nargs: '+'});
+ });
+ after(function () {
+ teardown_tempdir(oldcwd);
+ });
+ it("Test reading arguments from a file", function () {
+ args = parser.parseArgs(['X', 'Y']);
+ assert.deepEqual(args, {a: null, x: 'X', y: ['Y']});
+ args = parser.parseArgs(['X', '-a', 'A', 'Y', 'Z']);
+ assert.deepEqual(args, { a: 'A', x: 'X', y: ['Y', 'Z']});
+ args = parser.parseArgs(['@hello', 'X']);
+ assert.deepEqual(args, {a: null, x: 'hello world!', y: ['X']});
+ args = parser.parseArgs(['X', '@hello']);
+ assert.deepEqual(args, {a: null, x: 'X', y: ['hello world!']});
+ });
+ it("Test recursive reading arguments from files", function () {
+ args = parser.parseArgs(['-a', 'B', '@recursive', 'Y', 'Z']);
+ assert.deepEqual(args, {a: 'A', x: 'hello world!', y: ['Y', 'Z']});
+ args = parser.parseArgs(['X', '@recursive', 'Z', '-a', 'B']);
+ assert.deepEqual(args, {a: 'B', x: 'X', y: ['hello world!', 'Z']});
+ });
+ it('Test reading arguments from an invalid file', function () {
+ assert.throws(
+ function () {
+ args = parser.parseArgs(['@invalid']);
+ },
+ /ENOENT, no such file or directory/
+ );
+ });
+ it('Test reading arguments from an missing file', function () {
+ assert.throws(
+ function () {
+ args = parser.parseArgs(['@missing']);
+ },
+ /ENOENT, no such file or directory/
+ );
+ });
+ it('Test custom convertArgLineToArgs function', function () {
+ parser.convertArgLineToArgs = function (argLine) {
+ // split line into 'words'
+ args = argLine.split(' ');
+ args = args.map(function (arg) {return arg.trim(); });
+ args = args.filter(function (arg) {return arg.length > 0; });
+ return args;
+ };
+ args = parser.parseArgs(['X', '@hello']);
+ assert.deepEqual(args, {a: null, x: 'X', y: ['hello', 'world!']});
+ });
+});
+

0 comments on commit ab7465b

Please sign in to comment.