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

Support --only-show-errors #179

Merged
merged 3 commits into from
Mar 16, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/logging.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ Logging
- By default, log messages Warning and above are shown to the user.
- `--verbose` - This flag changes the logging level to Info and above.
- `--debug` - This flag changes the logging level to Debug and above.
- `--only-show-errors` - This flag changes the logging level to Error only, suppressing Warning.

* All log messages go to STDERR (not STDOUT)
* Log to Error or Warning for user messages instead of using the `print()` function
Expand Down
81 changes: 78 additions & 3 deletions examples/exapp2
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,22 @@ helps['abc list'] = """
text: {cli_name} abc list
""".format(cli_name=cli_name)

helps['abc first'] = """
type: command
short-summary: List the first several letters in the alphabet.
examples:
- name: Show the list of abc
text: {cli_name} abc first --number 3
""".format(cli_name=cli_name)

helps['abc last'] = """
type: command
short-summary: List the last several letters in the alphabet.
examples:
- name: Show the list of xyz
text: {cli_name} abc last --number 3
""".format(cli_name=cli_name)


def a_test_command_handler():
return [{'a': 1, 'b': 1234}, {'a': 3, 'b': 4}]
Expand All @@ -38,9 +54,55 @@ def abc_list_command_handler():
return list(string.ascii_lowercase)


def abc_first_command_handler(number=5):
import string
return list(string.ascii_lowercase)[0:number]


def abc_last_command_handler(number=5):
import string
return list(string.ascii_lowercase)[-number:]


def num_range_command_handler(start=0, end=5):
"""
Get a list of natural numbers from start to end
:param start: the lower bound
:param end: the higher bound
:return:
"""
return list(range(int(start), int(end) + 1))


def sample_json_handler():
"""
Get a sample JSON dict
"""
# https://docs.microsoft.com/en-us/rest/api/resources/subscriptions/list#examples
result = {
"id": "/subscriptions/291bba3f-e0a5-47bc-a099-3bdcb2a50a05",
"subscriptionId": "291bba3f-e0a5-47bc-a099-3bdcb2a50a05",
"tenantId": "31c75423-32d6-4322-88b7-c478bdde4858",
"displayName": "Example Subscription",
"state": "Enabled",
"subscriptionPolicies": {
"locationPlacementId": "Internal_2014-09-01",
"quotaId": "Internal_2014-09-01",
"spendingLimit": "Off"
},
"authorizationSource": "RoleBased",
"managedByTenants": [
{
"tenantId": "8f70baf1-1f6e-46a2-a1ff-238dac1ebfb7"
}
]
}
return result

def hello_command_handler(myarg=None, abc=None):
return ['hello', 'world', myarg, abc]


WELCOME_MESSAGE = r"""
_____ _ _____
/ ____| | |_ _|
Expand All @@ -67,15 +129,28 @@ class MyCommandsLoader(CLICommandsLoader):
def load_command_table(self, args):
with CommandGroup(self, 'hello', '__main__#{}') as g:
g.command('world', 'hello_command_handler', confirmation=True)
with CommandGroup(self, '', '__main__#{}') as g:
g.command('sample-json', 'sample_json_handler')
with CommandGroup(self, 'abc', '__main__#{}') as g:
g.command('list', 'abc_list_command_handler')
g.command('show', 'a_test_command_handler')
g.command('get', 'a_test_command_handler', deprecate_info=g.deprecate(redirect='show', hide='0.1.0'))
g.command('list', 'abc_list_command_handler')
g.command('show', 'a_test_command_handler')
g.command('get', 'a_test_command_handler', deprecate_info=g.deprecate(redirect='show', hide='1.0.0'))
g.command('first', 'abc_first_command_handler', is_preview=True)
g.command('last', 'abc_last_command_handler', )
with CommandGroup(self, 'ga', '__main__#{}') as g:
g.command('range', 'num_range_command_handler')
with CommandGroup(self, 'pre', '__main__#{}', is_preview=True) as g:
g.command('range', 'num_range_command_handler')
with CommandGroup(self, 'exp', '__main__#{}', ) as g:
g.command('range', 'num_range_command_handler')
return super(MyCommandsLoader, self).load_command_table(args)

def load_arguments(self, command):
with ArgumentsContext(self, 'hello world') as ac:
ac.argument('myarg', type=int, default=100)
with ArgumentsContext(self, 'ga range') as ac:
ac.argument('start', type=int, is_preview=True)
ac.argument('end', type=int, )
super(MyCommandsLoader, self).load_arguments(command)


Expand Down
1 change: 1 addition & 0 deletions knack/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ def __init__(self,
self.output = self.output_cls(cli_ctx=self)
self.result = None
self.query = query_cls(cli_ctx=self)
self.only_show_errors = self.config.get('core', 'only_show_errors', fallback=False)
self.enable_color = not self.config.get('core', 'no_color', fallback=False)

@staticmethod
Expand Down
11 changes: 6 additions & 5 deletions knack/invocation.py
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ def execute(self, args):
self.parser.load_command_table(self.commands_loader)
self.cli_ctx.raise_event(EVENT_INVOKER_CMD_TBL_LOADED, parser=self.parser)

arg_check = [a for a in args if a not in ['--verbose', '--debug']]
arg_check = [a for a in args if a not in ['--verbose', '--debug', '--only-show-warnings']]
if not arg_check:
self.cli_ctx.completion.enable_autocomplete(self.parser)
subparser = self.parser.subparsers[tuple()]
Expand Down Expand Up @@ -197,10 +197,11 @@ def execute(self, args):
preview_kwargs['object_type'] = 'command'
previews.append(ImplicitPreviewItem(**preview_kwargs))

for d in deprecations:
print(d.message, file=sys.stderr)
for p in previews:
print(p.message, file=sys.stderr)
if not self.cli_ctx.only_show_errors:
for d in deprecations:
print(d.message, file=sys.stderr)
for p in previews:
print(p.message, file=sys.stderr)

cmd_result = parsed_args.func(params)
cmd_result = todict(cmd_result)
Expand Down
45 changes: 33 additions & 12 deletions knack/log.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
import logging
from logging.handlers import RotatingFileHandler

from .util import CtxTypeError, ensure_dir
from .util import CtxTypeError, ensure_dir, CLIError
from .events import EVENT_PARSER_GLOBAL_CREATE

CLI_LOGGER_NAME = 'cli'
Expand Down Expand Up @@ -74,6 +74,7 @@ class CLILogging(object):

DEBUG_FLAG = '--debug'
VERBOSE_FLAG = '--verbose'
ONLY_SHOW_ERRORS_FLAG = '--only-show-errors'

@staticmethod
def on_global_arguments(_, **kwargs):
Expand All @@ -83,6 +84,9 @@ def on_global_arguments(_, **kwargs):
help='Increase logging verbosity. Use --debug for full debug logs.')
arg_group.add_argument(CLILogging.DEBUG_FLAG, dest='_log_verbosity_debug', action='store_true',
help='Increase logging verbosity to show all debug logs.')
arg_group.add_argument(CLILogging.ONLY_SHOW_ERRORS_FLAG, dest='_log_verbosity_only_show_errors',
action='store_true',
help='Only show errors, suppressing warnings.')

def __init__(self, name, cli_ctx=None):
"""
Expand All @@ -109,8 +113,8 @@ def configure(self, args):
:param args: The arguments from the command line
:type args: list
"""
verbose_level = self._determine_verbose_level(args)
log_level_config = self.console_log_configs[verbose_level]
log_level = self._determine_log_level(args)
log_level_config = self.console_log_configs[log_level]
root_logger = logging.getLogger()
cli_logger = logging.getLogger(CLI_LOGGER_NAME)
# Set the levels of the loggers to lowest level.
Expand All @@ -126,16 +130,23 @@ def configure(self, args):
self._init_logfile_handlers(root_logger, cli_logger)
get_logger(__name__).debug("File logging enabled - writing logs to '%s'.", self.log_dir)

def _determine_verbose_level(self, args):
def _determine_log_level(self, args):
""" Get verbose level by reading the arguments. """
verbose_level = 0
for arg in args:
if arg == CLILogging.VERBOSE_FLAG:
verbose_level += 1
elif arg == CLILogging.DEBUG_FLAG:
verbose_level += 2
# Use max verbose level if too much verbosity specified.
return min(verbose_level, len(self.console_log_configs) - 1)
# arguments have higher precedence than config
if CLILogging.ONLY_SHOW_ERRORS_FLAG in args:
if CLILogging.DEBUG_FLAG in args or CLILogging.VERBOSE_FLAG in args:
raise CLIError("--only-show-errors can't be used together with --debug or --verbose")
self.cli_ctx.only_show_errors = True
return 1
if CLILogging.DEBUG_FLAG in args:
self.cli_ctx.only_show_errors = False
return 4
if CLILogging.VERBOSE_FLAG in args:
self.cli_ctx.only_show_errors = False
return 3
if self.cli_ctx.only_show_errors:
return 1
return 2 # default to show WARNINGs and above

def _init_console_handlers(self, root_logger, cli_logger, log_level_config):
root_logger.addHandler(_CustomStreamHandler(log_level_config['root'],
Expand Down Expand Up @@ -167,6 +178,16 @@ def _get_log_dir(cli_ctx):
@staticmethod
def _get_console_log_configs():
return [
# --only-show-critical [RESERVED]
{
CLI_LOGGER_NAME: logging.CRITICAL,
'root': logging.CRITICAL
},
# --only-show-errors
{
CLI_LOGGER_NAME: logging.ERROR,
'root': logging.CRITICAL
},
# (default)
{
CLI_LOGGER_NAME: logging.WARNING,
Expand Down
4 changes: 2 additions & 2 deletions tests/test_deprecation.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,8 +98,8 @@ def test_deprecate_command_help_hidden(self):
cmd3' instead.

Arguments
-b [Required] : Allowed values: a, b, c.
--arg -a : Allowed values: 1, 2, 3.
-b [Required] : Allowed values: a, b, c.
--arg -a : Allowed values: 1, 2, 3.
--arg3
""".format(self.cli_ctx.name)
self.assertIn(expected, actual)
Expand Down
41 changes: 22 additions & 19 deletions tests/test_help.py
Original file line number Diff line number Diff line change
Expand Up @@ -295,6 +295,7 @@ def test_help_full_documentations(self):
Global Arguments
--debug : Increase logging verbosity to show all debug logs.
--help -h : Show this help message and exit.
--only-show-errors : Only show errors, suppressing warnings.
--output -o : Output format. Allowed values: json, jsonc, none, table, tsv, yaml,
yamlc. Default: json.
--query : JMESPath query string. See http://jmespath.org/ for more information and
Expand Down Expand Up @@ -322,18 +323,19 @@ def test_help_with_param_specified(self):
Long summary here. Still long summary.

Arguments
-b [Required] : Allowed values: a, b, c.
--arg -a : Allowed values: 1, 2, 3.
-b [Required] : Allowed values: a, b, c.
--arg -a : Allowed values: 1, 2, 3.
--arg3

Global Arguments
--debug : Increase logging verbosity to show all debug logs.
--help -h : Show this help message and exit.
--output -o : Output format. Allowed values: json, jsonc, none, table, tsv, yaml, yamlc.
Default: json.
--query : JMESPath query string. See http://jmespath.org/ for more information and
examples.
--verbose : Increase logging verbosity. Use --debug for full debug logs.
--debug : Increase logging verbosity to show all debug logs.
--help -h : Show this help message and exit.
--only-show-errors : Only show errors, suppressing warnings.
--output -o : Output format. Allowed values: json, jsonc, none, table, tsv, yaml, yamlc.
Default: json.
--query : JMESPath query string. See http://jmespath.org/ for more information and
examples.
--verbose : Increase logging verbosity. Use --debug for full debug logs.

"""
actual = io.getvalue()
Expand Down Expand Up @@ -437,19 +439,20 @@ def register_globals(_, **kwargs):
Long summary here. Still long summary.

Arguments
-b [Required] : Allowed values: a, b, c.
--arg -a : Allowed values: 1, 2, 3.
-b [Required] : Allowed values: a, b, c.
--arg -a : Allowed values: 1, 2, 3.
--arg3

Global Arguments
--debug : Increase logging verbosity to show all debug logs.
--exampl : This is a new global argument.
--help -h : Show this help message and exit.
--output -o : Output format. Allowed values: json, jsonc, none, table, tsv, yaml, yamlc.
Default: json.
--query : JMESPath query string. See http://jmespath.org/ for more information and
examples.
--verbose : Increase logging verbosity. Use --debug for full debug logs.
--debug : Increase logging verbosity to show all debug logs.
--exampl : This is a new global argument.
--help -h : Show this help message and exit.
--only-show-errors : Only show errors, suppressing warnings.
--output -o : Output format. Allowed values: json, jsonc, none, table, tsv, yaml, yamlc.
Default: json.
--query : JMESPath query string. See http://jmespath.org/ for more information and
examples.
--verbose : Increase logging verbosity. Use --debug for full debug logs.

"""
actual = io.getvalue()
Expand Down