Skip to content

Update show commands to exit with code 3 upon a 404.#6740

Merged
williexu merged 22 commits intoAzure:devfrom
williexu:generic_show
Jul 11, 2018
Merged

Update show commands to exit with code 3 upon a 404.#6740
williexu merged 22 commits intoAzure:devfrom
williexu:generic_show

Conversation

@williexu
Copy link
Copy Markdown
Contributor

@williexu williexu commented Jul 6, 2018


Closes: #6612
Closes: #5006

This checklist is used to make sure that common guidelines for a pull request are followed.

  • The PR has modified HISTORY.rst describing any customer-facing, functional changes. Note that this does not include changes only to help content. (see Modifying change log).

  • I adhere to the Command Guidelines.

@williexu williexu changed the title Update show command registration to exit with code 3 upon a 404. Update show commands to exit with code 3 upon a 404. Jul 6, 2018
Copy link
Copy Markdown
Member

@tjprescott tjprescott left a comment

Choose a reason for hiding this comment

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

Two main concerns:

  1. There are hints of serious breaking changes here. Anywhere you need to change a test from "check.is_empty" to expect failure is a red flag.

  2. custom_command=True creates multiple ways to do the same thing, is less flexible than the existing mechanism (getter_type=…) and doesn't seem like it actually simplifies the registration.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

What is this?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

It's the list of excluded params aside from 'self' and 'client', which are used for client_factory.
'generic_wait' had a bug where if the operation signature included 'client', it would not be used for the client_factory (only 'self' would).
generic_update was doing the right thing: https://github.com/Azure/azure-cli/blob/dev/src/azure-cli-core/azure/cli/core/commands/arm.py#L442

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

if possible, find a better name than the EXCLUDED_NON_CLIENT_PARAMS. It is a bit hard to read and hard to understand

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I don't see the simplification here. Previously if you have a custom getter, you would specify the getter name and getter_type=custom_type. This seems like it opens up the potential to have conflicting arguments. What happens if I put getter_type=custom_type and custom_command=False?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

The getter_type and setter_type will always take priority.
If getter_type=custom_type, then the custom_type will be used.
This should simplify things since most getter_type/setter_types are custom.
ex. https://github.com/Azure/azure-cli/blob/dev/src/command_modules/azure-cli-eventgrid/azure/cli/command_modules/eventgrid/commands.py#L35-L45

In the example above, 3 lines would be saved for the one generic_update command as there would be no need to redefine the custom_command_type.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Why isn't custom_command just another kwarg?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

kwargs are passed along. This ensures that custom_command is only used where it is intended; in the generic_* related methods..

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This is the role of the getter_type kwarg. I don't agree with providing multiple ways to do the same thing. This convenience flag only works when a module has a single custom command type. It breaks down (whereas getter_type does not) when custom commands are broken into multiple files (see storage and monitor).

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

True yes, getter_type can still be used as the default to provide more flexibility. custom_command is a convenience.
This is the same reason why we have the command and custom_command methods, you can still register a custom command using command and specifying a command_type.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Hmm… we should probably chat about this offline.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Is this a minor fix or a breaking change? Most of these changes signify breaking changes, so we can't necessarily rely on our boilerplate text here.

Copy link
Copy Markdown
Contributor Author

@williexu williexu Jul 9, 2018

Choose a reason for hiding this comment

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

True, thanks

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

The change needed to this test implies there is a serious breaking change here.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

True

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Breaking changes should bump the middle number, not the right-most number.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Is there anything actually generic about this? In the case of update, the generic piece are the --set, --add, --remove arguments. In the case of wait, it's the different generic wait conditions.

My preference here would be to expose show_command and custom_show_command which eliminates the need for the custom_command kwarg and neatly parallels the command/custom_command pattern.

I would leave generic update/wait as is.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Hmm, depends on how we define "generic". It's generic in terms of providing a base functionality for all commands registered with it. The signature for the commands are also similar.
It does not, however, provide any additional arguments.

I like the idea of exposing show_command/custom_show_command but it does seem inconsistent if update/wait do not follow the *_command/custom_*_command pattern.

@troydai @yugangw-msft, what do you guys think about the addition of a 'custom_command' kwarg?

Currently, generic_* commands will only make use of the command_type kwarg. custom_command_type, whether defined in the command_loader, or the command_group() is ignored. The custom_command kwarg will refer to the custom_command_type by default, for cases like this.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

They are dubbed generic because of the syntax (especially for "generic" update), not because they enforce some common behavior. The add arguments that may not actually work for a given situation (for example, you create a resource with --no-wait and then wait on --deleted... dumb thing to do, but the command can't know).

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I do agree that wait and show should use the same mechanisms, because in probably 99% of cases they likely are using the exact same getters. I would rather advocate for this scheme in lieu of the flag:

show_command(…)
custom_show_command(…)
wait_command(…)
custom_wait_command(…)
generic_wait_command(…) # deprecate and phase out
generic_update_command(…) # remains unaltered

The main difference is that show and wait only rely on a single callable, so it is appropriate to say whether that kwarg bundle comes from the normal command_type bundle or the custom_command_type bundle since we do that for 'regular' commands.

However, generic update weaves together 2-3 independent callables to accomplish something. There should only be one default for this, and that default, which fits 90% of the cases is that the setter and getter come from command_type and the custom piece comes from custom_command_type. If you deviate from that pattern, you have to get specific. I'd bet at least half of the usages of generic update that use custom getters and setters could be rewritten to not use them at all. There are a small number of cases where Swagger naming issues necessitate using the name kwarg, but the default still holds for the types.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Having both wait and show use the same custom_command/command pattern sounds good to me as they have the same syntax. I agree that generic_update is much more complex in syntax to, and thus can be grouped apart from, the other two.
I'll leave generic_update as is.

One more thing, is there a need to "deprecate" generic_wait_command as it's only seen by developers? Not sure how deprecating it would look like, and I can replace the method now with this PR.

@williexu williexu force-pushed the generic_show branch 4 times, most recently from 054d4f5 to 3c9d997 Compare July 10, 2018 18:22
Copy link
Copy Markdown
Member

@tjprescott tjprescott left a comment

Choose a reason for hiding this comment

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

Much closer! I like the direction it is headed. I do also want to confirm that generic_wait_command still exists and has the same signature it did before. We can't remove it without potentially breaking extensions. We should also add a warning to that registration path to point command authors to the new commands.

Finally (and this could be a separate but high priority PR) these new command group methods should be added to the documentation.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I'd recommend you get rid of getter type for these since the type is specified by whether wait_command or custom_wait_command is used.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Same. No longer need getter_type.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

It seems like rather than having lots of logic like this:

kwargs.get('custom_command_type', None) if custom_command else kwargs.get('command_type', None)

Where you risk a logic error if there's a typo, you could just leverage the source_kwarg argument you removed elsewhere. That way you specify it in fewer places and your logic looks like:

kwargs.get(source_kwarg, None)

You also wouldn't need that big if-else block in _get_operations_tmpl because you would just pass it the source_kwarg directly.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Yes, make this accept source_kwarg (no default) and have show_command pass 'command_type' and custom_show_command pass 'custom_command_type'. It will simplify your code. Ditto for the private wait command.

Copy link
Copy Markdown
Contributor Author

@williexu williexu Jul 10, 2018

Choose a reason for hiding this comment

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

The problem with passing along source_kwarg is that it does not give any indication what all the possible choices are for it.
Validation also needs to be done on that source_kwarg in the case that a spelling typo is made unless we are relying on this being caught elsewhere (not ideal).

Using a flag gives the user clear information that there are two possible choices and leaves less room for error.
To lessen the <>if custom_command else <> statements, I've added a util method that selects the right source_kwarg that will be utilized in all the cases.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Works for me.

Copy link
Copy Markdown
Member

@tjprescott tjprescott Jul 10, 2018

Choose a reason for hiding this comment

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

You should alter the signature of custom_wait_command so that the second positional arg is the getter_name without specifying it. Then it looks and works just like custom_show_command and custom_command.

Copy link
Copy Markdown
Contributor Author

@williexu williexu Jul 10, 2018

Choose a reason for hiding this comment

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

getter_name IS the second postional argument in the signature, similar to the show commands and generic_update. They all(show/update/wait) also allow the use of the argument as a keyword, so something like custom_wait_command('wait', '_server_postgresql_get' is also valid.
I've left the usage of the argument as a keyword as the author of the command did.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Ah, in that case, I'd just recommend removing it so it looks the rest.

williexu added 3 commits July 10, 2018 14:59
style
called wrong method
@williexu
Copy link
Copy Markdown
Contributor Author

@tjprescott addressed your comments, please take another look

Copy link
Copy Markdown
Member

@tjprescott tjprescott left a comment

Choose a reason for hiding this comment

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

The docs look fine. A couple comments left--you need to preserve the docstrings on the public methods because they provide intellisense through the IDE. I don't think having the docstring on the private method will accomplish that.


# pylint: disable=arguments-differ
def command(self, name, method_name=None, **kwargs):
"""
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

You should not delete this docstring. It is used to provide intellisense. Publicly exposed methods should have docstrings, including your new ones :)

def _command(self, name, method_name, custom_command=False, **kwargs):
"""
Register a custom CLI command.
Register a CLI command.
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This on the other hand does not need a docstring (but there's no harm in leaving it).


return command_name
def custom_command(self, name, method_name=None, **kwargs):
return self._command(name, method_name=method_name, custom_command=True, **kwargs)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

You need to preserve the docstring here. You might also try copying the _command docstring to this. You just need to make sure the intellisense still works for the command and I think by doing this, you do away with it.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Can confirm that intellisense does not work(at least in vscode), but for what cases would we try to ensure intellisense is enabled?
We have many publicly exposed methods in our CLI, with very few containing docstrings, command/custom_command were the very rare exceptions.

IMO, intellisense does not seem very important unless we expect people to use the CLI as a library to call methods from.
As there is no harm in them, I can add docstring documentation for the command registration methods if you feel strongly otherwise.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

TL/DR: intellisense is important and we should have the docstrings on those methods

We should have docstrings on any public methods we expect command authors to use. So the public methods of command group and argument context should have docstrings to provide intellisense. A classic example is the resource_id method that now lives in msrestazure.tools. This originally was strongly typed, which provided some kind of intellisense. When Johan converted it to just use kwargs, it became impossible to use without pulling up code somewhere to know what you can use. A docstring was added so that the user gets intellisense. This is just as important for these methods that rely heavily on kwargs.

setter_op = self._resolve_operation(merged_kwargs, setter_name, setter_type)
custom_func_op = self._resolve_operation(merged_kwargs, custom_func_name, custom_func_type,
source_kwarg='custom_command_type') if custom_func_name else None
custom_type=True) if custom_func_name else None
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

It would be good to stick to one nomenclature--either custom_type or custom_command. My recommendation would actually be is_custom_command since that's more clearly a Boolean. custom_type vs. custom_func_type.... doesn't really make sense why they are different data types.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Works for me.

def generic_wait_command(self, name, getter_name='get', getter_type=None, **kwargs):
self._generic_wait_command(name, getter_name=getter_name, getter_type=getter_type, **kwargs)

def _generic_wait_command(self, name, getter_name='get', getter_type=None, custom_command=False, **kwargs):
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

can we rename to _wait_command or any other w/o the generic? I am a bit sensitive to generic_xxx naming as those are supposed to be public facing. But up to you though since i don't work on basic command infrastructure that much.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

if possible, find a better name than the EXCLUDED_NON_CLIENT_PARAMS. It is a bit hard to read and hard to understand

return result


def get_source_kwarg(custom_command):
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

why not name it get_command_type?

Copy link
Copy Markdown
Contributor Author

@williexu williexu Jul 11, 2018

Choose a reason for hiding this comment

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

great idea, had it as source_kwarg due to the argument name present before I made changes. I'll change it to get_command_type_kwarg, as it returns the keyword, not the command_type itself.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

show commands in the CLI will fail with an exit code of 3 upon a missing resource(404). az <resource> show has inconsistent exit status

4 participants