-
-
Notifications
You must be signed in to change notification settings - Fork 203
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Use argparse to configure container-traits from command-line #322
Conversation
… _classes_with_config_traits()
@minrk At The same question applies for all such tests. |
+ Both `class.trait` & aliases added as argparse-args.
+ Both `class.trait` & aliases added as argparse-args.
+ Both `class.trait` & aliases added as argparse-args.
+ Only `class.trait` added as argparse-args - not aliases yet.
@minrk PLEASE REVIEW the code + specs in the OP, to eventual become material for the manual. |
PR works fine - maybe some TCs could also test more fringe cases, e.g. what happens when a List trait without defaults and without In general, for a next release it might make more sense to drop the "mixed" parsing of args when conf-classes are available and delegate everything to arparse. By the way, why in loader.py#L517 it uses eval to set the Config tree? |
+ Use a regex as a `type` argument in `argparse.add_argument()` to parse each kv-expression. + Add `KVAgParseLoader` "index" field `argparse_traits` to use it when building the `config` items for Dict traits. + Assuming alias {'D': 'Foo.bar'} and `Foo.bar = Dict()` then one can write: cmd -D k1=v1 -D K2=123
+ Use a regex as a `type` argument in `argparse.add_argument()` to parse each kv-expression. + Add `KVAgParseLoader` "index" field `argparse_traits` to use it when building the `config` items for Dict traits. - Had to add globals/locals into `exec()` setting the config value. + Assuming alias {'D': 'Foo.bar'} and `Foo.bar = Dict()` then one can write: cmd -D k1=v1 -D K2=123
ankostis asked:
Answering myself, I found 329db36 where @ellisonbg in 2009(!) did this change: - if v is not NoDefault:
- setattr(self.config, k, v)
+ if v is not NoConfigDefault:
+ exec_str = 'self.config.' + k + '= v'
+ exec exec_str in locals(), globals() unfortunately without explaining the reason - the only relative part of the commit message must be this:
When using
Any explaination why? |
+ Use a regex as a `type` argument in `argparse.add_argument()` to parse each kv-expression. + Add `KVAgParseLoader` "index" field `argparse_traits` to use it when building the `config` items for Dict traits. - Had to add globals/locals into `exec()` setting the config value. + Assuming alias {'D': 'Foo.bar'} and `Foo.bar = Dict()` then one can write: cmd -D k1=v1 -D K2=123
With the latest commit, also |
A simple setattr doesn't work because it's nested. It can be A.B.c. You could parse out the name and make a nested series of setattrs. I'm not sure that would help anything. I don't think it's relevant to this PR. |
Thanks. So You see, after my changes, in py2.7 I was getting the following error in
|
I hadn't merged #325 yet, so rebasing wouldn't have picked it up. It's merged now, though. |
… _classes_with_config_traits()
+ Only `class.trait` added as argparse-args - not aliases yet.
+ Use a regex as a `type` argument in `argparse.add_argument()` to parse each kv-expression. + Add `KVAgParseLoader` "index" field `argparse_traits` to use it when building the `config` items for Dict traits. - Had to add globals/locals into `exec()` setting the config value. + Assuming alias {'D': 'Foo.bar'} and `Foo.bar = Dict()` then one can write: cmd -D k1=v1 -D K2=123
+ Add ellipses and explain the element type. + Add TCs with output samples.
+ Reason: if container-traits are handled by *argparse*, then any regular trait with name that is a prefix of some container-trait would get miss-classified as that container-trait. + Retrofitted app-TCs to detect the above clash. - Had to disable 1 TC: test-app.test_ipython_cli_priority()
Indeed, the problem is fixed, TC passes. |
I've tested and this is working great, thanks! Pinging @jhamrick, who I believe has used the current |
Cool! I had basically given up on trying to use lists and dictionaries on the command line, so this won't cause any problems for me. Thanks! 🎉 |
Since you're both reviewing, a note from my side for the multiple places where cmd-line values are parsed:
Maybe the places where these parsings take place can be centralized a bit, regarding the Loader hierarchy of classes (see my Future section on the opening post. |
Is there any plans for releasing this? |
@ankostis not immediately, we still have a few things to get through. |
List/Set/Tuple/Dict
traits and use them to setup argparse for reading cmd-line args either with action='append' or nargs='*'`.The reason is that any "regular"(not handled by argparse) trait with a name that is a prefix of some container-trait will get miss-classified as that container-trait (disabling this
allow_abbrev
behavior is not supported in py2).Design
Application
onparse_command_line()
uses itsclasses
field to collects all relevantHasTraits
classes and subclasses to push them into theKVArgParseConfigLoader
constructor;KVArgParseConfigLoader.load_config()
invokes_add_arguments()
for all list/Set/tuple traits found on the collected classes , and appends them as argparse arguments according to themultiplicity
metadata found on these traits:multiplicity == 'append'
: useadd_argument(..., action='append')
(default if metadata missing);multiplicity == [ '*' | '+' | N ]
: useadd_argument(..., nargs=multiplicity)
.argparse_traits
to be used in the next step to create the appropriate config-tree instance.KVArgParseConfigLoader._convert_to_config()
py-evaluates any lists-of-values (or list-of-tuples forDict
traits) one by one before creating and assigning the appropriate instance (i.e.dict
forDict
traits) into the returned config-instance .Example
Assuming this 2 configurables + 1 app:
Then, in command-line it should be possible to enter all these commands:
Sub
class trumps over anyBase
values.For a
Dict
trait, the cmd-line syntax is this:resulting in this:
Future
KVArgParseConfigLoader
intoArgParseConfigLoader
class - actually I would try to compress the loader hierarchy.