bw2 / ConfigArgParse

A drop-in replacement for argparse that allows options to also be set via config files and/or environment variables.
MIT License
719 stars 121 forks source link

Enable Recursive config calls #261

Open NielsPichon opened 2 years ago

NielsPichon commented 2 years ago

Goal

This PR aims at making config inheritance possible. To do so it makes to contributions:

Motivations

This is useful for instance when using a piece of software for many different configurations which can be divided into some sort of parent and children classes.

Details

As an example we could have something like

general_conf_for cars
             |
           /   \
         /       \
       /           \  
sports_car_conf    consumer_cars_conf    Brand
                            \                /
                              \            /
                                \        /
                                   \   /
                                     |
                                    Model

With the PR we can now specify specify something like -my-config consumer_cars brand for the model to have both the config or the brand and the consumer_cars_conf. The order matters as the last specified config file will take precedence of the previous ones. Alternatively you can specify it in a config file of its own:

model.ini

my-config=[consumer_cars, brand]
model_name=model

This will pull out and parse the config for brand and that for consumer cars. The consumer_cars_conf itself could be something like:

consumer_cars_conf.ini

my-config=general_conf_for cars
top-speed=130

As such this allows the user to build a full inheritance system.

Parsing order and circular dependencies

In terms of evaluation order for the arguments we have the following:

  1. command_line args
  2. model.ini args (e.g. top-speed)
  3. brand.ini args
  4. consumer_car_conf.ini args
  5. general_conf_for_cars
  6. default_values

In the general case, for each recursion level, the existing command_line args take precedence, then all the config files are parsed in the reversed order of appearance on the command line, and their args are added to the command line if not there already, and then we start a new recursion round with the newly parsed confs.

Note that we have put some guards to avoid circular dependencies (eg config1 pointing to config2 which points to config 1, creating an endless loop), but the first pass (the config files specified in the original command line arguments) are not registered as already parsed. This is not really an issue as the arguments from the first config file will already be in the command_line_args if it get parsed again and will thus not be updated.

Testing

I have also added some unittest to make sure everytthing is parsed as expected and there are no infinite loop if the config files define circular dependencies.

bw2 commented 2 years ago

Sorry I think this functionality is too complicated for the main configargparse repo.