Skip to content
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

Prefer mut_arg over help_about and version_about #2211

Merged
merged 4 commits into from
Feb 8, 2021
Merged

Prefer mut_arg over help_about and version_about #2211

merged 4 commits into from
Feb 8, 2021

Conversation

pksunkara
Copy link
Member

@pksunkara pksunkara commented Nov 14, 2020

The original motivation for this PR is that changing the help & version args short, long and hidden and other stuff wasn't working with mut_arg.

In v2, we have help_short, version_short, help_message & version_message which used to set these. But it's definitely not a complete solution because other details like hidden etc.. can't be set. So, we removed them in favor of mut_arg. But since mut_arg executes before building the help and version args, it needs the whole arg built and not just the one attribute you want to change. (I have added tests cases to reflect this).

One other thing we want to think on is recurring changes. Users don't want to keep doing this for every app/subcommand they build. Ideally they want it to propagate from top. We added help_about and version_about in v3 which propagates. These were requested for v2 originally because users weren't able to set help & version subcommand's about.

So, I decided to get some opinion before actually finalising this:

  1. Do we agree on mut_arg being executed during the build process after building all the args?
  2. Do we need a global_mut_arg that propagates and does the same as mut_arg?
  3. Do we need a mut_subcommand and global_mut_subcommand that behaves similarly but for subcommands? (This will allow us to replace help_about and version_about to use this).

@pksunkara pksunkara marked this pull request as draft November 14, 2020 08:37
@pksunkara pksunkara self-assigned this Nov 14, 2020
@kbknapp
Copy link
Member

kbknapp commented Nov 17, 2020

It sounds like the conflicting ideas are that the help and version args are built lazily (just before parsing), but mut_arg would need them to exist in order to actually mutate them in any meaningful way. This is consistent with the problems I ran into at the beginning of the v3 build.

The options are we either:

  • Only run mut_arg at the last possible time (i.e. after all args are built), but this has the problem of not allowing one to mutate an arg based on existing args. i.e. if an App struct is built, passed to some other function for further processing and queried for information, and based off that information mutated. All of that would have to happen well before parsing.
  • Build the help and version "dummy" args early in the process. The biggest downside is if someone does not want help/version args this is needless processing. However, it's extremely minimal, so I would be fine with this personally. The other problem that could arise is not knowing if the values provided were clap consumer supplied, or the dummy values meaning we'd need to track the changes.
  • A combination of the two - where the dummy args are built early, can be mutated freely, and just prior to parsing they are filled/normalized to remove or fill any remaining dummy values.

To address the having to do this globally, or not I see the two ideas as orthogonal. If you mutate a global argument, that mutation should happen prior to global propagation. If that mutation happens after global propagation, that is on purpose (i.e. to change/undo some unwanted propagated value). We just need to be clear about when mutation happens vs when propagation happens. I don't think a mut_arg_global is warranted. The only somewhat exception to this rule may be the help and version args, which we could document and make global from the start, but I'm not sure how this would work in the specifics of a subcommand where the user wants to mutate the help/version args from the parent command, and wants those changes passed on to any other children...but this might be super niche to the point we don't need to worry about it in the general case.

  1. Do we agree on mut_arg being executed during the build process after building all the args?

I would suggest running mut_arg eagerly i.e. as soon as it's called. Otherwise you end up with "stacking" changes that can be hard to mentally track in your own application. However, I do agree help/version should already be built (which I think leads to building help/version early as mentioned above, instead of lazily.

  1. Do we need a global_mut_arg that propagates and does the same as mut_arg?

I don't believe so. If you mut_arg a global arg, those changes will be propagated down once the propagation phase happens.

  1. Do we need a mut_subcommand and global_mut_subcommand that behaves similarly but for subcommands? (This will allow us to replace help_about and long_about to use this).

Interesting. I hadn't thought about this too much yet. I think eventually a mut_subcommand would be helpful to provide API parity. I have the same feelings about the global_* though.

@pksunkara
Copy link
Member Author

I think there is a misunderstanding about global_mut_arg. It is not for global arguments but it is for propagating the mut_arg lambda to all the subcommands. That way users can use this to specify the short for version once at the top App and all the subcommands will follow it.

@kbknapp
Copy link
Member

kbknapp commented Nov 17, 2020

I get that, but I mean if an argument is global (i.e. it will be propagated to all child subcommands) and a mutate lambda gets applied to it in the parent, that mutation is "applied" in the parent command, and the already mutated arg is what is actually propagated down to the child subcommand. i.e. there is no need to re-apply that mutation in each child subcommand.

The only part this gets a little wacky, is as you noted with how we handle help/version since those aren't real args until the parsing starts. So I'm suggesting potentially making help/version right away as global args when App is initialized (perhaps with dummy values that we can distinguish later are parse time to know if something was user supplied or not), that way they too can take advantage of this global vs mutation behavior.

Otherwise, I agree and we would only be able to apply the mutations after help/version are built (i.e. parse time) and would also need some construct for propagating these mutations to child subcommands if applicable which I think is more complex.

Edit: Let me know if I'm still off track and missing something 😄

@pksunkara
Copy link
Member Author

I am stuck on making them global args. How would I turn them off in my subcommands? I think people rely on help and version being a separate arg instead of global.

tests/version.rs Outdated Show resolved Hide resolved
tests/version.rs Outdated Show resolved Hide resolved
@pksunkara
Copy link
Member Author

@ldm0 Any thoughts on this?

@ldm0
Copy link
Member

ldm0 commented Jan 23, 2021

  1. Do we agree on mut_arg being executed during the build process after building all the args?

I agree. I think seperating the help and the version out from average args is a bad idea.

  1. Do we need a global_mut_arg that propagates and does the same as mut_arg?

I think it's needed. I get what @kbknapp said. But I think applying global_mut_arg doesn't equals to applying mut_arg on a global argument. Changing different args in different subcommands with the same name is also a valid use case.

  1. Do we need a mut_subcommand and global_mut_subcommand that behaves similarly but for subcommands? (This will allow us to replace help_about and long_about to use this).

I think they are needed, whether from the standpoint of API elegance or from the standpoint of usefulness.

@pksunkara
Copy link
Member Author

So I'm suggesting potentially making help/version right away as global args when App is initialized

Just to confirm, you are saying this is a bad idea, right?

@ldm0
Copy link
Member

ldm0 commented Jan 26, 2021

So I'm suggesting potentially making help/version right away as global args when App is initialized

Just to confirm, you are saying this is a bad idea, right?

Nope, I think this is a great idea. These two idea doesn't conflicts. We can have mut_arg after building help and version as current workaround, and than trying to make the help and version global args later.

@pksunkara
Copy link
Member Author

That doesnt need to be done later. They can be done now. But that is what I have been trying to do and I find myself stuck on the API design.

@ldm0
Copy link
Member

ldm0 commented Jan 26, 2021

That doesnt need to be done later. They can be done now. But that is what I have been trying to do and I find myself stuck on the API design.

Changing the behavior of DisableHelpFlags and DisableVersion. Letting them turn corresponding arg off in the app may helps.

@pksunkara pksunkara changed the title Apply mut_arg after building help and version Prefer mut_arg over help_about and version_about Feb 7, 2021
@pksunkara pksunkara marked this pull request as ready for review February 7, 2021 18:03
@pksunkara pksunkara requested a review from ldm0 February 7, 2021 18:03
Copy link
Member

@ldm0 ldm0 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Several questions:

src/build/arg/mod.rs Show resolved Hide resolved
@pksunkara pksunkara merged commit 9aea23c into master Feb 8, 2021
@pksunkara pksunkara deleted the mut_arg branch February 8, 2021 05:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants