Comments (13)
I'm not sure if I can reopen this issue, maybe you could reopen it @martinjm97?
from typed-argument-parser.
I understand. I think my use case actually does not really correspond to subparsers, because of that reason. What I want is to be able to specify arguments in json or yaml files with >1 level of nesting, or like --model.lr
which then gets parsed into a nested Tap Config.
Like
{
"dataset": "mnist",
"model": {
"layers": 6,
}
}
Which would get parsed into a Tap config with fields that you could access like config.dataset
or config.model.layers
. Of course, this would mainly be useful if you can parse json/yaml files, but even with cli arguments like --model.layers
I think it could already be useful. I don't like 100s of unstructured argument fields thrown together in a single Config class without structure.
from typed-argument-parser.
@martinjm97 I agree with @rubencart. It's a frequent use-case to define nested config in deep learning projects. Wish you can support this extension in the future :)
Since Tap supports load/save config, type hint, and auto complete, sometimes I use Tap not only as arg-parser, but also a unified config class, and even dynamically generate some config value during runtime:
# use as argparser
class MyArgParser(Tap):
do_train: bool = False
do_test_only: bool = False
disable_gpu: bool = False
debug: bool = Fasle
comment: str = ""
# use as config class
@dataclass
class Config_Training(Tap):
learning_rate: float = 2e-5
epoch: int = 3
optimizer: str = "adam"
...
def parse_args(self):
# when used as a pure config class, I will disable this function.
raise NotImplementError()
# use as runtime calculated config class
@dataclass
class Config_FilePath(Tap):
debug: bool
start_time: str
comment: str
def __post_init__(self):
self.OUTPUT_DIR = Path("./output") / f"{self.start_time}_debug-{self.debug}_{self.comment}"
self.CONFIG_DIR = self.OUTPUT_DIR / "config"
self.CHECKPOINT_DIR = self.OUTPUT_DIR / "checkpoint"
self.log_path = self.OUTPUT_DIR / "log.txt"
...
def parse_args(self):
# when used as a pure config class, I will disable this function.
raise NotImplementError()
if __name__=="__main__":
start_time = datetime.now().strftime('%b-%d_%H-%M-%S')
args = MyArgParser().parse_args()
config_train = Config_train()
config_filepath = Config_FilePath(debug=args.debug, start_time=start_time, comment=args.comment)
# save config
args.save(config_filepath.CONFIG_DIR / "args.json")
config_train.save(config_filepath.CONFIG_DIR / "config_train.json")
config_filepath.save(config_filepath.CONFIG_DIR / "config_filepath.json")
I know these are far away from standard Tap
use-case, but they are very convenient!
from typed-argument-parser.
My solution so far:
class SubArgs(Tap):
subarg1: int
subarg2: str
class Args(Tap):
arg1: int
arg2: str
subargs: str
def add_arguments(self) -> None:
self.add_argument("--subargs", type=lambda x: SubArgs, required=True)
def process_args(self) -> None:
tmp = SubArgs()
tmp.from_dict(self.subargs)
self.subargs = tmp
args = Args().parse_args()
print(args.arg1, args.subargs.subarg1)
This seemed to work but the resulting Args
object could not be pickled. Adding the following method to Args
solved it.
def __getstate__(self):
dct = {
k: v for (k, v) in self.as_dict().items()
}
dct.update({'model': self.model.as_dict()})
return dct
from typed-argument-parser.
Hi @rubencart,
If I understand you correctly, you're looking for the functionality of a subparser. We implemented subparsers here. Let me know if I'm misunderstanding you. Your solution is quite inventive though :)
--Jesse
from typed-argument-parser.
Feel free to reopen if subparsers don't actually resolve the issue.
from typed-argument-parser.
If I'm understanding it correctly, they're not what I'm looking for. It seems like you can add multiple subparsers, but that you can actually only 'choose' one when you're executing the command, based on which subcommand you choose. Is that correct?
I want multiple subparsers that are all activated when I run my main command, to be able to direct different arguments to the parts of my application where they are relevant.
from typed-argument-parser.
This relates in a way to #39 and #25, especially when loading json/yaml config files, it would be nice if you could structure those config files, and then access the parsed configs in python afterwards according to that structure IMO 🙂 .
from typed-argument-parser.
I agree this could be a very useful functionality!
from typed-argument-parser.
you can actually only 'choose' one when you're executing the command, based on which subcommand you choose. Is that correct?
Yes!
I want multiple subparsers that are all activated when I run my main command, to be able to direct different arguments to the parts of my application where they are relevant.
At a high-level, we don't believe there's a clean way to implement this and it extends beyond the functionality of argparse
. The spec is ambiguous because it's not clear whether a subparser or subparser of a subparser is being selected. For example, consider:
from tap import Tap
class OtherSubparser(Tap):
bar: str
class SubparserA(Tap):
baz: int
def configure(self):
self.add_subparser('b', OtherSubparser)
class SubparserB(Tap):
bar: int
class Args(Tap):
foo: bool = False
def configure(self):
self.add_subparser('a', SubparserA)
self.add_subparser('b', SubparserB)
args = Args().parse_args('a --baz 3 b --bar 14'.split())
Is b
activating SubparserB
or OtherSubparser
? While this is sort of an odd case, it is still a case that would have to be handled by an implementation. What do you think?
Best,
Kyle and Jesse
from typed-argument-parser.
Hi @rubencart,
This makes perfect sense to me. This is definitely an extension to argparse
(correct me if I'm mistaken) and our primary goal is to be a typed wrapper to argparse
. I think it's a great additional feature, but we may not get to it in the near future since it is an extension (e.g., we'll want to support Python 3.9 types first). Thank you for the awesome idea!
Best,
Jesse
from typed-argument-parser.
I understand! :)
from typed-argument-parser.
Thank you again for bringing up this interesting use case. We've decided that we won't be adding this feature to Tap. We're happy to reconsider/reopen the issue. Our reasoning and recommendations are below.
Our primary goal is to be a typed wrapper to argparse
. In order to both be consistent with argparse
and maintain typing for nested arguments, we would need to introduce significant complexity. Specifying nested argument from the command line is complicated and it is beyond the default behavior of argparse
(although you can always write a custom type parsing function).
If you need typing, you could write a custom class and unpacking function.
If you're willing to forego typing, our recommendation is to use easydict, which works as follows:
in config.json
,
{
"dataset": "mnist",
"model": {
"layers": 6
}
}
in Python,
import json
from easydict import EasyDict as edict
with open('config.json') as f:
args = edict(json.load(f))
print(args.model.layers)
from typed-argument-parser.
Related Issues (20)
- Add useful type hints to `Tap.add_subparser` HOT 5
- Refactor `tapify` to enable subparsers HOT 1
- Type safe way to access subparser arguments? HOT 2
- Fix `tapify` to correctly handle **kwargs HOT 1
- [Suggestion] Allow to reference previous arguments as default values HOT 1
- Allow for multi-line documentation of the function HOT 1
- Tuple parsing with literals HOT 1
- [help] Adding `version` arg to print version but bypassing required args HOT 3
- tapify help string order is random HOT 1
- More convenient syntax for subparsers
- ImportError: cannot import name 'Tap' from 'tap' HOT 3
- What's the best way to create a parser from a Pydantic model? HOT 3
- Human readable JSON for saved Python object HOT 1
- Add support for using the `Annotated` type to provide comments for the help string HOT 1
- Tapify, docstrings and typing.NamedTuples
- [suggestion] Class variable arguments with multiline defaults do not respect help comments HOT 1
- add_argument should not overwrite properties defined in class member. HOT 1
- Versions should not be compared as strings HOT 1
- Tap eats my custom error messages :( HOT 4
- [Feature Request] Add new config file types HOT 1
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from typed-argument-parser.