Skip to content

๐ŸฆŽ Minimal Python command-line parser inspired by Facebook's Hydra. Handles and parses arbitrary arguments into dot-accessible nested dictionaries.

License

Notifications You must be signed in to change notification settings

vict0rsch/minydra

Folders and files

NameName
Last commit message
Last commit date

Latest commit

ย 

History

92 Commits
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 

Repository files navigation

minydra ๐ŸฆŽ

Minimal Python command-line parser inspired by Facebook's Hydra + dot-accessible nested dictionaries.

Easily parse arbitrary arguments from the command line without dependencies:

example code example code

pip install minydra

minydra is tested on Python 3.7, 3.8 and 3.9.


Getting Startedย ย โ€ขย  Forcing typesย ย โ€ขย  MinyDictย ย โ€ขย  Save configย ย โ€ขย  Prevent typosย ย โ€ขย  Use default configsย ย โ€ขย  Examples


Getting Started

examples/parser.py

from minydra.parser import Parser

if __name__ == "__main__":
    parser = Parser(
        verbose=0, # print received args
        allow_overwrites=False, # allow repeating args in the command-line
        warn_overwrites=True, # warn repeating args if they are allowed
        parse_env=True, # get environment variable
        warn_env=True, # warn if an environment variable is specified but not found
        defaults=None, # path to a MinyDict-loadable dictionary of default values for the args
        strict=True, # if `defaults` is provided, whether to allow new keys in the command-line
                     # or restrict to `defaults`' keys
        keep_special_kwargs=True, # `defaults` and `strict` can be set from the command-line
                                  # with `@defaults=` and `@strict=`. This argument decides if
                                  # you want to keep those keys in the final arguments.
    )
    args = parser.args.pretty_print().resolve().pretty_print() # notice .resolve() transforms dotted.keys into nested dicts

examples/resolved_args.py

from minydra import resolved_args

if __name__ == "__main__":
    args = resolved_args()
    args.pretty_print()

examples/demo.py examples/demo.json

from minydra import MinyDict, resolved_args
from pathlib import Path

if __name__ == "__main__":
    # parse arbitrary args in 1 line
    args = resolved_args()

    # override default conf
    if args.default:
        args = MinyDict.from_json(args.default).update(args)

    # protect args in the rest of the code execution
    args.freeze()

    # print the args in a nice orderly fashion
    args.pretty_print()

    # access args with dot/attribute access
    print(f'Using project "{args.log.project}" in {args.log.outdir}')

    # save configuration
    args.to_json(Path(args.log.outdir) / f"{args.log.project}.json")

examples/decorator.py

import minydra
from minydra.dict import MinyDict

@minydra.parse_args(verbose=0, allow_overwrites=False) # Parser's init args work here
def main(args: MinyDict) -> None:
    args.resolve().pretty_print()


if __name__ == "__main__":
    main()



Parsing

  • Simple strings are parsed to float and int automatically.
  • A single keyword will be interpreted as a positive flag.
  • A single keyword starting with - will be interpreted as a negative flag.
  • If parse_env is True, environment variables are evaluated.
$ python examples/decorator.py outdir=$HOME/project save -log learning_rate=1e-4 batch_size=64
โ•ญโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎ
โ”‚ batch_size    : 64                        โ”‚
โ”‚ learning_rate : 0.0001                    โ”‚
โ”‚ log           : False                     โ”‚
โ”‚ outdir        : /Users/victor/project     โ”‚
โ”‚ save          : True                      โ”‚
โ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏ
  • dotted keys will be resolved to nested dictionary keys:
$ python examples/decorator.py server.conf.port=8000
โ•ญโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎ
โ”‚ server             โ”‚
โ”‚ โ”‚conf              โ”‚
โ”‚ โ”‚ โ”‚port : 8000     โ”‚
โ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏ
  • Using ast.literal_eval(value), minydra will try and parse more complex values for arguments as lists or dicts. Those should be specified as strings:
$ python examples/decorator.py layers="[1, 2, 3]" norms="{'conv': 'batch', 'epsilon': 1e-3}"
โ•ญโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎ
โ”‚ layers : [1, 2, 3]                               โ”‚
โ”‚ norms  : {'conv': 'batch', 'epsilon': 0.001}     โ”‚
โ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏ

Forcing types

Adding ___<type> to a key will force this type to the value. Notice how 01 is parsed to an integer 1 but 04 is parsed to a string (as specified) "04", and hello is parsed to a list, not kept as a string

$ python examples/decorator.py n_jobs___str=04 job=01 chips___list=hello
โ•ญโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎ
โ”‚ chips  : ['h', 'e', 'l', 'l', 'o']     โ”‚
โ”‚ job    : 1                             โ”‚
โ”‚ n_jobs : 04                            โ”‚
โ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏ

Known types are defined in Parser.known_types and the separator (___) in Parser.type_separator

In [1]: from minydra import Parser

In [2]: Parser.known_types
Out[2]: {'bool', 'float', 'int', 'str'}

In [3]: Parser.type_separator
Out[3]: '___'

Command-line configuration

You can configure the Parser from the command-line using special @ arguments. In other words, all __init__(self, ...) arguments can be set from the command-line with @argname=new_value.

In particular if you run python examples/decorator.py @defaults=./examples/demo.json you will see:

โ•ญโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎ
โ”‚ @defaults : ./examples/demo.json     โ”‚
โ”‚ log                                  โ”‚
โ”‚ โ”‚logger                              โ”‚
โ”‚ โ”‚ โ”‚log_level   : DEBUG               โ”‚
โ”‚ โ”‚ โ”‚logger_name : minydra             โ”‚
โ”‚ โ”‚outdir  : /some/path                โ”‚
โ”‚ โ”‚project : demo                      โ”‚
โ”‚ verbose   : False                    โ”‚
โ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏ

But if you add @strict=false @keep_special_kwargs=false you will now have:

$ python examples/decorator.py @defaults=./examples/demo.json @strict=false @keep_special_kwargs=false
โ•ญโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎ
โ”‚ log                          โ”‚
โ”‚ โ”‚logger                      โ”‚
โ”‚ โ”‚ โ”‚log_level   : DEBUG       โ”‚
โ”‚ โ”‚ โ”‚logger_name : minydra     โ”‚
โ”‚ โ”‚outdir  : /some/path        โ”‚
โ”‚ โ”‚project : demo              โ”‚
โ”‚ verbose : False              โ”‚
โ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏ

(you need to have @strict=false since @keep_special_kwargs is unknown in demo.json. It would not be the case if strict=false had been used in the script itself (but it can be overridden from the command-line!))



MinyDict

Minydra's args are a custom lightweight wrapper around native dict which allows for dot access (args.key), resolving dotted keys into nested dicts and pretty printing sorted keys in a box with nested dicts indented. If a key does not exist, it will not fail, rather return None (as dict.get(key, None)).

a MinyDict inherits from dict so usual methods work .keys(), .items() etc.

In [1]: from minydra.dict import MinyDict

In [2]: args = MinyDict({"foo": "bar", "yes.no.maybe": "idontknow"}).pretty_print(); args
โ•ญโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎ
โ”‚ foo          : bar           โ”‚
โ”‚ yes.no.maybe : idontknow     โ”‚
โ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏ
Out[2]: {'foo': 'bar', 'yes.no.maybe': 'idontknow'}

In [3]: args.resolve().pretty_print(); args
โ•ญโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎ
โ”‚ foo : bar                โ”‚
โ”‚ yes                      โ”‚
โ”‚ โ”‚no                      โ”‚
โ”‚ โ”‚ โ”‚maybe : idontknow     โ”‚
โ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏ
Out[3]: {'foo': 'bar', 'yes': {'no': {'maybe': 'idontknow'}}}

In [4]: args.yes.no.maybe
Out[4]: "idontknow"

In [5]: "foo" in args
Out[5]: True

In [6]: "rick" in args
Out[6]: False

In [7]: args.morty is None
Out[7]: True

In [8]: args.items()
Out[8]: dict_items([('foo', 'bar'), ('yes', {'no': {'maybe': 'idontknow'}})])

Dumping/Loading

You can save and read MinyDict to/from disk in 3 formats: json and pickle without dependencies, yaml with the PyYAML dependency (pip install minydra[yaml]).

Methods to_pickle, to_json and to_yaml have 3 arguments:

  1. file_path as a str or pathlib.Path which is resolved:
    1. expand env variable ($MYDIR for instance)
    2. expand user (~)
    3. make absolute
  2. return_path which defaults to True. If those methods return the path of the created file
  3. allow_overwrites which defaults to True. If False and path exists, a FileExistsError will be raised. Otherwise creates/overwrites the file at file_path
  4. verbose which defaults to 0. If >0 prints the path of the created object

Note:

  • to/from_yaml will fail with a ModuleNotFoundError if PyYAML is not installed.
  • the json standard does not accept ints as keys in dictionaries so {3: 2} would be dumped -- and therefore loaded -- as {"3": 2}.
In [1]: from minydra.dict import MinyDict

In [2]: args = MinyDict({"foo": "bar", "yes.no.maybe": "idontknow"}).resolve(); args
Out[2]: {'foo': 'bar', 'yes': {'no': {'maybe': 'idontknow'}}}

In [3]: json_file_path = args.to_json("./args.json")

In [4]: yaml_file_path = args.to_yaml("./args.yaml")

In [5]: pkl_file_path = args.to_pickle("./args.pkl")

In [6]: _ = args.to_json("./args.json", verbose=1) # verbose argument prints the path
Json dumped to: /Users/victor/Documents/Github/vict0rsch/minydra/args.json

In [7]: MinyDict.from_json("args.json")
Out[7]: {'foo': 'bar', 'yes': {'no': {'maybe': 'idontknow'}}}

In [8]: assert (
    MinyDict.from_yaml(yaml_file_path)
    == MinyDict.from_json(json_file_path)
    == MinyDict.from_pickle(pkl_file_path)
    == args
)

examples/dumps.py

python examples/dumps.py path="./myargs.pkl" format=pickle cleanup

โ•ญโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎ
โ”‚ cleanup : True             โ”‚
โ”‚ format  : pickle           โ”‚
โ”‚ path    : ./myargs.pkl     โ”‚
โ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏ
Dumped args to /Users/victor/Documents/Github/vict0rsch/minydra/myargs.pkl
Cleaning up

Strict Mode

To prevent typos from the command-line, the MinyDict.update method has a strict mode: updating a MinyDict with another one using strict=True will raise a KeyError if the key does not already exist:

from minydra import MinyDict, resolved_args

if __name__ == "__main__":
    # parse arbitrary args in 1 line
    args = resolved_args()

    # override default conf
    if args.default:
        path = args.default
        # delete otherwise it will be used to update the conf which does not have
        # "default" as a key, therefore raising a KeyError in strict mode
        del args.default
        args = MinyDict.from_json(path).update(args, strict=True)

    args.pretty_print()

No typo:

$ python examples/strict.py default=./examples/demo.json log.logger.log_level=INFO
โ•ญโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎ
โ”‚ log                          โ”‚
โ”‚ โ”‚logger                      โ”‚
โ”‚ โ”‚ โ”‚log_level   : INFO        โ”‚
โ”‚ โ”‚ โ”‚logger_name : minydra     โ”‚
โ”‚ โ”‚outdir  : /some/path        โ”‚
โ”‚ โ”‚project : demo              โ”‚
โ”‚ verbose : False              โ”‚
โ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏ

Typo:

$ python examples/strict.py default=./examples/demo.json log.logger.log_leveel=INFO
Traceback (most recent call last):
  File "/Users/victor/Documents/Github/vict0rsch/minydra/examples/strict.py", line 13, in <module>
    args = MinyDict.from_json(path).update(args, strict=True)
  File "/Users/victor/Documents/Github/vict0rsch/minydra/minydra/dict.py", line 111, in update
    self[k].update(v, strict=strict)
  File "/Users/victor/Documents/Github/vict0rsch/minydra/minydra/dict.py", line 111, in update
    self[k].update(v, strict=strict)
  File "/Users/victor/Documents/Github/vict0rsch/minydra/minydra/dict.py", line 100, in update
    raise KeyError(
KeyError: 'Cannot create a non-existing key in strict mode ({"log_leveel":INFO}).'

Using default configurations

The minydra.Parser class takes a defaults= keyword argument. This can be:

  • a str or a pathlib.Path to a json yaml or pickle file that minydra.MinyDict can load (from_X)
  • a dict or a minydra.MinyDict
  • a list of the above types, in which case the resulting defaults will be the result of sequential updates from those defaults, enabling hierarchical defaults (first defaults are the starting point, then each subsequent defaults updates it)

When defaults is provided, the resulting minydra.MinyDict serves as a reference for the arguments parsed from the command-line:

  • If you setup the parser with strict=True, arguments from the command-line will still have a higher priority but they will have to be present in the defaults to prevent typos or unknown arguments (see strict mode)
  • arguments not present in the command-line with fallback to values in defaults

defaults can actually be a list and the update order is the same as the list's. For instance:

In [1]: from minydra import Parser

In [2]: Parser(defaults=["./examples/demo.json", "./examples/demo2.json"]).args.pretty_print();
โ•ญโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎ
โ”‚ log                             โ”‚
โ”‚ โ”‚logger                         โ”‚
โ”‚ โ”‚ โ”‚log_level   : INFO           โ”‚
โ”‚ โ”‚ โ”‚logger_name : minydra        โ”‚
โ”‚ โ”‚outdir  : /some/other/path     โ”‚
โ”‚ โ”‚project : demo                 โ”‚
โ”‚ new_key : 3                     โ”‚
โ”‚ verbose : False                 โ”‚
โ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏ

If you need to set defaults from the command-line, there's a special @defaults keyword you can use:

$ python examples/decorator.py @defaults=./examples/demo.json
โ•ญโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎ
โ”‚ @defaults : ./examples/demo.json     โ”‚
โ”‚ log                                  โ”‚
โ”‚ โ”‚logger                              โ”‚
โ”‚ โ”‚ โ”‚log_level   : DEBUG               โ”‚
โ”‚ โ”‚ โ”‚logger_name : minydra             โ”‚
โ”‚ โ”‚outdir  : /some/path                โ”‚
โ”‚ โ”‚project : demo                      โ”‚
โ”‚ verbose   : False                    โ”‚
โ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏ

$ python examples/decorator.py @defaults="['./examples/demo.json', './examples/demo2.json']"
โ•ญโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎ
โ”‚ @defaults : ['./examples/demo.json', './examples/demo2.json']     โ”‚
โ”‚ log                                                               โ”‚
โ”‚ โ”‚logger                                                           โ”‚
โ”‚ โ”‚ โ”‚log_level   : INFO                                             โ”‚
โ”‚ โ”‚ โ”‚logger_name : minydra                                          โ”‚
โ”‚ โ”‚outdir  : /some/other/path                                       โ”‚
โ”‚ โ”‚project : demo                                                   โ”‚
โ”‚ new_key   : 3                                                     โ”‚
โ”‚ verbose   : False                                                 โ”‚
โ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏ

pretty_print

Prints the MinyDict in a box, with dicts properly indented. A few arguments:

  1. indents, which defaults to 2: the amount of indentation for nested dictionaries
  2. sort_keys, which defaults to True: whether or not to alphabetically sort the keys before printing

to_dict

To produce a native Python dict, use args.to_dict()


Protected attributes

MinyDict's methods (including the dict class's) are protected, they are read-only and you cannot therefore set attributes with there names, like args.get = 2. If you do need to have a get argument, you can access it through items: args["get"] = 2.

Try with examples/protected.py:

python examples/protected.py server.conf.port=8000 get=3
โ•ญโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎ
โ”‚ get    : 3         โ”‚
โ”‚ server             โ”‚
โ”‚ โ”‚conf              โ”‚
โ”‚ โ”‚ โ”‚port : 8000     โ”‚
โ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏ
<built-in method get of MinyDict object at 0x100ccd4a0>
3
dict_items([('get', 3), ('server', {'conf': {'port': 8000}})])
{'conf': {'port': 8000}}



Tests

Run tests and pre-commit checks (isort, black, flake8) with

$ pip install -r requirements-test.txt
$ pre-commit run --all-files
$ pytest -vv --cov=minydra tests/

About

๐ŸฆŽ Minimal Python command-line parser inspired by Facebook's Hydra. Handles and parses arbitrary arguments into dot-accessible nested dictionaries.

Topics

Resources

License

Stars

Watchers

Forks

Languages