I really appreciate Python’s pathlib module for managing filesystem stuff. While I don’t love the argparse module for command line parsing, I don’t think it’s worse than other available options. I usually choose it for my CLI scripts, since nothing else is good enough to overcome the inertia of using a third party library.

Not many people seem to be aware that the two can very easily be combined such that argparse will return Path objects instead of strings that need to be adapted after you query them:

import argparse
from pathlib import Path


def main():
    parser = argparse.ArgumentParser()
    parser.add_argument(
        "--data_dir",
        type=Path,
        default=Path(__file__).absolute().parent / "data",
        help="Path to the data directory",
    )

    p = parser.parse_args()
    print(p.data_dir, type(p.data_dir))


if __name__ == "__main__":
    main()

The key is the type=Path parameter, which automatically converts whatever string the user supplies into a Path object. All command line arguments come in as strings. The type parameter just takes a function that accepts a string and returns a value. Thus, type=int, which you may have seen in the argparse docs does the same thing as int('thestring').

You can also define your own functions. For example, I prefer my paths to always be in absolute form:

    parser.add_argument(
        "--data_dir",
        type=lambda p: Path(p).absolute(),
        default=Path(__file__).absolute().parent / "data",
        help="Path to the data directory",
    )

Now my options object contains exactly what I want it to and I don’t have to write an adapter each time I access the path:

 $ python path_example.py
/home/dusty/code/path_example/data <class 'pathlib.PosixPath'>

 $ python path_example.py --data_dir=relative
/home/dusty/code/path_example/relative <class 'pathlib.PosixPath'>

 $ python path_example.py --data_dir=/tmp/absolute
/tmp/absolute <class 'pathlib.PosixPath'>