1

I have a program with ~20 configs, which I would like to be accessed from the command-line via argparse, either by something like --config_1 'This is the first config' or --config-location <path_to_config_file>, where the path to the config file would be a .txt of form:

--config_1 'This is the first config'
--config_2 '2nd Config'
--config_3 3
...

Currently, my code looks something like:

parser = argparse.ArgumentParser()
parser.add_argument('--config_1')
args = vars(parser.parse_args())

This approach was inspired by youtube-dl's --config-location. If there's a better way to accomplish what I'm trying to do, suggestions are also appreciated and welcome. Thank you for reading!

Wilan
  • 147
  • 1
  • 12

2 Answers2

2

argparse supports something similar out of the box.

parser = ArgumentParser(fromfile_prefix_chars='@')
parser.parse_args()

Now any argument starting with @ will be treated as a filename to be read, which each line being treated as a separate argument. This would require your file to look like

--config_1
This is the first config
--config_2
2nd Config
--config_3
3
...

instead.

What youtube-dl does is simply open the file, read its contents, and split it on whitespace similar to how the shell would.

BrokenBenchmark
  • 18,126
  • 7
  • 21
  • 33
chepner
  • 497,756
  • 71
  • 530
  • 681
  • Thank you for your (quick) reply! Is it possible to allow `--config-location` as well as `--config_1`, but if `--config-location` is specified, ignore `--config_1`? Also, I'm not sure what you mean by "any argument starting with @ will be treated as a filename to be read" - how are arguments treated as filenames? – Wilan Mar 25 '22 at 17:52
  • With the parser shown, `yourscript @foo.txt` would be equivalent to `yourscript --config_1 'This is the first config' --config_2 '2nd config' --config_3 3`. – chepner Mar 25 '22 at 17:58
  • The machinery for processing such a file isn't really exposed by `ArgumentParser`, so if you want `--config-location foo.txt` to be the same as `@foo.txt`, you'd have to implement your own processing of `foo.txt` after parsing the arguments. – chepner Mar 25 '22 at 17:59
  • I managed to get it to work - thank you! A follow up question if I may, how would I pass a dictionary as an argument from `foo.txt`? I'm able to get it to work for strings, ints, etc. but not dictionaries. – Wilan Mar 25 '22 at 18:35
  • Since everything you pass is a string which needs to be parsed, you'd have to do something like pass a JSON object and use `type=json.loads` when you define the option. – chepner Mar 25 '22 at 20:15
0

With that many command line options, a better approach would be to make a configuration file. There are two ways of doing this:

The first is to create a separate Python file. Create a file called config.py:

config_1 = 'This is the first config'
config_2 = '2nd config'
config_3 = 3

Then, in another file (say, prog.py):

import config

print(config.config_1)
print(config.config_2)
print(config.config_3)

This will output:

This is the first config
2nd config
3

The second way is an improvement upon the above approach. You can use ConfigParser. For example, with an example config.ini:

[DEFAULT]
config_1 = 'This is the first config'
config_2 = '2nd config'
config_3 = 3

and an example prog.py:

import configparser

config = configparser.ConfigParser()
config.read('config.ini')
print(config['DEFAULT']['config_1'])
print(config['DEFAULT']['config_2'])
print(config['DEFAULT']['config_3'])

We can get:

This is the first config
2nd config
3

See here for a discussion between these two approaches.

BrokenBenchmark
  • 18,126
  • 7
  • 21
  • 33