Skip to content
Advertisement

Javascript argparse with JSON config

I’m trying to integrate argparse into my Javascript program, such that all arguments can be loaded from an external config file, and then those values can be overridden with provided CLI arguments afterwards. To do this, I decided to read a config file & parse it into a JSON object, then iterate through the keys and set the default values in the parser, and finally parse any remaining CLI arguments to override the defaults. Unfortunately, I’m getting some strange behavior back, and since it’s a port of the Python repository there’s not much syntactically-relevant documentation (https://www.npmjs.com/package/argparse)

I have the following logic:

//readFile helper method not shown
const configArgs = JSON.parse(readFile('config.json'))

for (let key in configArgs) {
    let value = configArgs[key]
    //used for debugging
    console.log(`${key}: ${value}`)
    parser.set_defaults({key: value})
  }

var args = parser.parse_args()
//used for debugging
console.log(args)

However, it looks like the parser.set_defaults() line isn’t working correctly:

path: ../data/testInput.txt
json: ../build/testSet.json
name: foo.txt
output: output.txt
Namespace(path=undefined, json=undefined, name=undefined, output=undefined, key='output.txt')

Why is it trying to create a new config option “key”, even though the key passed into set_defaults() is a variable & has a new value every time it’s logged to the console?

Answer

I contributed to this repository years ago, but haven’t worked with it or javascript recently.

In python argparse, I think this is what you want to do:

In [27]: conf = {'path': '../data/testInput.txt',
    ...: 'json': '../build/testSet.json',
    ...: 'name': 'foo.txt',
    ...: 'output': 'output.txt'}
In [28]: parser = argparse.ArgumentParser()
In [29]: for k in conf.keys():
    ...:     print(k,conf[k])
    ...:     parser.set_defaults(**{k:conf[k]})
    ...:     
path ../data/testInput.txt
json ../build/testSet.json
name foo.txt
output output.txt

In [30]: parser.parse_args([])
Out[30]: Namespace(path='../data/testInput.txt', json='../build/testSet.json', name='foo.txt', output='output.txt')

or simply set all keys at once:

In [31]: parser = argparse.ArgumentParser()
In [32]: parser.set_defaults(**conf)    
In [33]: parser.parse_args([])
Out[33]: Namespace(path='../data/testInput.txt', json='../build/testSet.json', name='foo.txt', output='output.txt')

In Python (**conf) is equivalent to (path='..data...', json='../build', etc).

The action of set_defaults is to add to the _defaults attribute:

In [34]: parser._defaults
Out[34]: 
{'path': '../data/testInput.txt',
 'json': '../build/testSet.json',
 'name': 'foo.txt',
 'output': 'output.txt'}

The python code is:

def set_defaults(self, **kwargs):
    self._defaults.update(kwargs)
    # and set defaults of actions if any

The corresponding javascript code is:

set_defaults(kwargs) {
    Object.assign(this._defaults, kwargs)
    # and set action.default if any

I don’t remember enough javascript to say whether that’s correct or not.

Advertisement