omni-us / jsonargparse

Implement minimal boilerplate CLIs derived from type hints and parse from command line, config files and environment variables
https://jsonargparse.readthedocs.io
MIT License
292 stars 39 forks source link

Change relative path in config from command line #529

Open AlejandroBaron opened 1 month ago

AlejandroBaron commented 1 month ago

🚀 Feature request

change relative path in config from command line

Motivation

I have a set of objects defined in individual yamls and I'd like to be able to change them from command line

Pitch

Let my config.yaml be

my_object: my_objects/my_object1.yaml

And then, a bunch of MyObjects defined in the my_objects/ directory

my_objects/
  - my_object1.yaml
  - my_object2.yaml
  - my_object3.yaml

I'd like to be able to change my_object field as follows

python main.py --config config.yaml --my_object my_objects/my_object2.yaml
mauvilsa commented 1 month ago

Thank you for proposing a feature! Unfortunately it is not clear to me exactly what is the idea. You might need to provide a complete runnable example.

Also, note that relative paths within configs and command line are already supported with the path types. For example, with the following existing files in the current directory:

main.py
subdir1/
  file1.txt
  file2.txt
  config.yaml
subdir2/
  file3.txt

and main.py:

#!/usr/bin/env python3

from jsonargparse import ArgumentParser, ActionConfigFile
from jsonargparse.typing import Path_fr

parser = ArgumentParser()
parser.add_argument("--files", type=list[Path_fr])
parser.add_argument("--config", action=ActionConfigFile)
cfg = parser.parse_args()
for file in cfg.files:
    print(f"{file}\t\t{file.absolute}")

and config.yaml:

files:
- file1.txt
- ../subdir1/file2.txt

The following outputs are obtained:

$ ./main.py --config subdir1/config.yaml | sed "s|$PWD/||" | column -t
file1.txt             subdir1/file1.txt
../subdir1/file2.txt  subdir1/../subdir1/file2.txt

$ ./main.py --config subdir1/config.yaml --files+=subdir2/file3.txt | sed "s|$PWD/||" | column -t
file1.txt             subdir1/file1.txt
../subdir1/file2.txt  subdir1/../subdir1/file2.txt
subdir2/file3.txt     subdir2/file3.txt

Relative paths inside config.yaml are validated with respect to the location of the config subdir1/. And paths given from command line are validated with respect to the directory from which the command is run ./.

If a parser includes subclass types (instantiatible objects) which have a path parameter, e.g.:

class MyObject:
    def __init__(self, file: Path_fr):
        ...

parser.add_subclass_arguments(MyObject, "object")  # supports --object path/to/object.yaml
parser.add_argument("--object", type=MyObject, enable_path=True)  # also supports --object path/to/object.yaml

the same relative path parsing already works inside configs and command line.