-
-
Notifications
You must be signed in to change notification settings - Fork 56
Description
🚀 Feature request
I would like a way for a class to have programatic mechanism such that a developer can tell jsonargparse what it's signature is and so they have control over how its arguments are parsed.
I want to describe something I'm currently doing to extend jsonargparse that demonstrates this idea, and brainstorm how something like this would best be intergrated into jsonargparse. And if it is the case that this feature isn't desired, then I hope to at least make the maintainers aware of what I'm doing so maybe they are less likely to break my monkey patches.
Motivation
Currently jsonargparse does a fine job of adding class arguments, provided those arguments are introspectable at runtime. I really like how it can peek into the docstring and extract useful help message. However, there are a few things I would like more control of:
- a mechanism for specifying the parameter name exactly once, and couple that with the documentation.
- aliases and shortform names
- customized argparse actions.
Pitch
I maintain yet another argument parsing library called scriptconfig. It works similarly to attrs
, but it focuses on giving a flat key/value configuration so a function can be invoked via kwargs, sys.argv, or a config file in the same way. It is declarative. You define a class, and the class variables assign metadata to attributes, which can then be instantiated / validated / used to make an argparse object / etc... Because all of the information about a variable is stored as a class attribute, you can couple the help docs with the declaration of the variable. You can also give each variable an alias, which helps a lot when you want to change a variable name, but you don't want to break existing users.
import scriptconfig as scfg
class MyClassConfig(scfg.DataConfig):
key1 = scfg.Value(1, alias=['key_one'], help='description1')
key2 = scfg.Value(None, help='description2')
key3 = scfg.Value(False, isflag=True, help='description3')
I use this to also define init signatures for complex classes that might have a lot of different - and evolving - arguments. This is particularly useful with pytorch lightning modules and datamodules, where I tend to use the following pattern to ensure everything is specified in a config dictionary.
class MyClass:
def __init__(self, **kwargs):
self.config = MyClassConfig(**kwargs)
I've recently switched to LightningCLI, and to ensure that my modules can be used with jsonargparse I've done something like this:
class MyClass:
__scriptconfig__ = MyClassConfig
def __init__(self, **kwargs):
self.config = MyClassConfig(**kwargs)
And I've extended and monkey patched jsonargparse to modify it's _add_signature_arguments
methods to look for the special __scriptconfig__
attribute in a class, and then do custom handling of the arguments in _add_signature_parameter
. I've posted a minimal example of the change in this gist.
The main idea of the change is that after it does introspection of the signature (for which sometimes there are positional arguments I don't add to the larger config), it looks at the config and uses it's metadata to construct an additional ParamData
item (and it extends it with some extra information) and then it does custom handling to build the args ultimately passed to add_argument
.
Currently it just handles the alias case, but I'm planning on extending the custom handling to mirror how scriptconfig handles arguments by doing some additional modification with things like custom argparse actions.
Summary
I use a concise attrs-like pattern to declaratively define a signature for a class __init__
method, which contains extra metadata like documentation and flags that can modify the argparse behavior. I would like to find a way to extend jsonargparse such that there is some special duner variable you can specify on a class you intend to pass to jsonargparse such that the user now has control to tell jsonargparse what the class signature is, as well as other metadata relating to documentation, typing, and ultimately customizing the way it jsonargparse constructs its ArgumentParser, including with custom actions.