Skip to content

Conversation

@nielsnl68
Copy link

Problem

In my setup i use a fixed configuration file that is loaded from an INI file. To make thinks easier for me i like the result of the arg parser have the same key names as this configuration class members so i could easily set the values without first modify the result.

Solution

With this PR we can now set an alternative attribute but name.

This small PR adds a method useName() to have a different attribute name then the current option name and the attributeName() checks first if the useName() is used will then us the given attrName or use the old method to determine the attributes name.

ChangeLog

Added a method to have a different attribute name then the option name.
@shadowspawn
Copy link
Collaborator

I don't think this is a common need, but it is the second time this has come up recently. See: #2423

@shadowspawn
Copy link
Collaborator

Looking into existing code and patterns.

For -s, --some-option:

Command class uses getter/setter pattern for some properties, but this pattern is not used in Option class.

commander.js/lib/command.js

Lines 2331 to 2335 in 0692be5

name(str) {
if (str === undefined) return this._name;
this._name = str;
return this;
}

All Option data properties are public.

@shadowspawn
Copy link
Collaborator

A small challenge with having public data properties on Option is that the obvious name for the data property and for the related chaining method are often the same. But you can not call both the data property and the chaining method the same thing!

@nielsnl68
Copy link
Author

A small challenge with having public data properties on Option is that the obvious name for the data property and for the related chaining method are often the same. But you can not call both the data property and the chaining method the same thing!

Good point, not sure what to name the variable i was doubting to name it alternatedName. But it did not give a good explanation of what it is. Do you have a preference?

@shadowspawn
Copy link
Collaborator

Just stick an underscore on the start for now to let the tests run. I'll come back to naming. (We work hard on the naming!)

@nielsnl68
Copy link
Author

I don't think this is a common need, but it is the second time this has come up recently. See: #2423

I fully understand that. i just your component fine without it. And would not have created this PR, when there was more involved then this.

I just played a little bit with the Python ArgParse library and it did have something like this as well. And made my code a lot easier to setup.

This is currently what i use in my nodejs script to get the configuration:

const config = {
    commport: 'none',
    baudrate: 115200,
    ini: "./NowTalk.ini",
    database: './nowTalk.sqlite',
    switchboardName: 'SwitchBoard',
    callname: "computer",
    dynamicExtIP: false,
    externelIP : "",
    allowGuests: true,
    webAddress: "*",//127.0.0.1
    webPort: 1215,
    allowNewDevice: true,
    badgeTimeout: 60,
    reconnectTimeout: 5000,
    connectTimeout: 1500
};

function defaultConfig(args) {
    let inifile = args.ini;
    var newConfig;

    if (inifile && fs.existsSync(inifile)) {
        newConfig = ini.parse(fs.readFileSync(inifile, 'utf-8'));
    } else {
        newConfig = {};
    }

    config.commport = args.port || newConfig.commport || config.commport;
    config.baudrate = makeNumber(args.baud || newConfig.baudrate || config.baudrate);
    ... etc.
}

defaultConfig({ ini: './nowTalk.ini' });

let portnames = []
const ports = await SerialPort.list();
for (const port of ports) {
    portnames.push(port.path);
}


program
    .version(JSON.stringify(pkg.version), '-v, --version', 'Show the current version.')
    .name(JSON.stringify(pkg.name))
    .usage('[options]')
    .description( JSON.stringify(pkg.description) + ' Pressing ctrl+c exits.')
    .addOption(new Option('-p, --port <commport>', 'Commport name of the serial port', config.commport).choices(portnames))
    .option('-b, --baud <baudrate>', 'Used baudrate', config.baudrate)
    // etc.
    .option('--ini <inifile>', 'Alternate ini filename')
    .option('-w --write', 'Write configuration settings to file')
    .parse(process.argv);

const args = program.opts();

defaultConfig(args);

if (args.write) {
    fs.writeFileSync(args.ini, ini.stringify(config));
}

And this was what could do in Python:

config = {
    'commport': 'none',
    'baudrate': 921600,
    # etc. 
}

def loadConfig(inifile) :
  global config
  try:
    if (inifile):
      ini = configparser.ConfigParser(defaults=config, allow_unnamed_section=True)
      ini.read(inifile)
      if (ini.has_section(configparser.UNNAMED_SECTION)):
          config = ini[configparser.UNNAMED_SECTION]
  except FileNotFoundError:
    pass

loadConfig('./NowTalk.ini')

parser = argparse.ArgumentParser(description='NowTalk Switchboard Tool', epilog="Copyright 2025, LumenSoft Nederland. ")
parser.add_argument('-port', type=str, help='The COM port to use', choices=list, dest='commport')
parser.add_argument('-baud', type=int, help='Baud rate (921600)', nargs="?", default=config['baudrate'], dest='baudrate')
# etc.
parser.add_argument('--ini', type=str, help='Alternate ini filename')

args = parser.parse_args()
if (args.ini is not None):
    loadConfig(args.ini)
else:
    config = config | vars(args)

@shadowspawn
Copy link
Collaborator

(Thanks for details on scenario. Interesting.)

@shadowspawn
Copy link
Collaborator

Python argparse documentation for dest, per example: https://docs.python.org/3/library/argparse.html#dest

@shadowspawn
Copy link
Collaborator

Yargs supports option "alias". This gets used to specify short+long options, but also for alternative names.

https://yargs.js.org/docs/#api-reference-aliaskey-alias

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants