Only ask questions if we are an interactive process (#30)
* Only ask questions if we are an interactive process * add tests for the question script * tweak color * add a breaking changes entry * typo * make ci happy
This commit is contained in:
parent
2984f547c5
commit
ca854efbc6
8 changed files with 90 additions and 8 deletions
2
.yapfignore
Normal file
2
.yapfignore
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
.git
|
||||
docs
|
||||
8
color
8
color
|
|
@ -5,21 +5,23 @@ PYTHON_ARGCOMPLETE_OK
|
|||
"""
|
||||
from milc import cli
|
||||
|
||||
colors = ('black', 'blue', 'cyan', 'green', 'magenta', 'red', 'white', 'yellow')
|
||||
|
||||
|
||||
@cli.entrypoint('Show all the colors available to us.')
|
||||
def main(cli):
|
||||
cli.echo('|Normal | FG | ExtFG | BG | ExtBG |')
|
||||
for color in ('black', 'blue', 'cyan', 'green', 'magenta', 'red', 'white', 'yellow'):
|
||||
for color in colors:
|
||||
cli.echo(f'|{color:8}|{{fg_{color}}}xxxxxxxx{{fg_reset}}|{{fg_light{color}_ex}}xxxxxxxx{{fg_reset}}|{{bg_{color}}}xxxxxxxx{{bg_reset}}|{{bg_light{color}_ex}}xxxxxxxx{{bg_reset}}|')
|
||||
|
||||
print()
|
||||
cli.echo('|Bright | FG | ExtFG | BG | ExtBG |')
|
||||
for color in ('black', 'blue', 'cyan', 'green', 'magenta', 'red', 'white', 'yellow'):
|
||||
for color in colors:
|
||||
cli.echo(f'|{color:8}|{{style_bright}}{{fg_{color}}}xxxxxxxx{{style_reset_all}}|{{style_bright}}{{fg_light{color}_ex}}xxxxxxxx{{style_reset_all}}|{{style_bright}}{{bg_{color}}}xxxxxxxx{{style_reset_all}}|{{style_bright}}{{bg_light{color}_ex}}xxxxxxxx{{style_reset_all}}|')
|
||||
|
||||
print()
|
||||
cli.echo('|Dim | FG | ExtFG | BG | ExtBG |')
|
||||
for color in ('black', 'blue', 'cyan', 'green', 'magenta', 'red', 'white', 'yellow'):
|
||||
for color in colors:
|
||||
cli.echo(f'|{color:8}|{{style_dim}}{{fg_{color}}}xxxxxxxx{{style_reset_all}}|{{style_dim}}{{fg_light{color}_ex}}xxxxxxxx{{style_reset_all}}|{{style_dim}}{{bg_{color}}}xxxxxxxx{{style_reset_all}}|{{style_dim}}{{bg_light{color}_ex}}xxxxxxxx{{style_reset_all}}|')
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ This is a list of breaking changes that have been made to MILC. If your script s
|
|||
|
||||
* The `config` subcommand now filters out configuration that has not been explicitly set. The new `--all` flag will allow you to see all possible configuration options and their default values.
|
||||
* Setting program metadata through environment variables has been deprecated. In its place is the new `set_metadata()` function. See [Metadata](metadata.md) for more detail.
|
||||
* MILC now tracks whether a script is running interactively or not with `cli.interactive`. You can pass `--interactive` to force a script into interactive mode even when stdout is not a TTY. `milc.questions` will always return the default answer when running non-interactively, unless `--yes` or `--no` are passed.
|
||||
|
||||
# Version 1.3.0
|
||||
|
||||
|
|
|
|||
|
|
@ -56,6 +56,7 @@ class MILC(object):
|
|||
self.default_arguments = {}
|
||||
self.platform = platform()
|
||||
self.prog_name = name
|
||||
self.interactive = sys.stdout.isatty()
|
||||
self.release_lock()
|
||||
|
||||
# Initialize all the things
|
||||
|
|
@ -223,6 +224,7 @@ class MILC(object):
|
|||
self.add_argument('--log-file', help='File to write log messages to')
|
||||
self.add_argument('--color', action='store_boolean', default=ansi_config['color'], help='color in output')
|
||||
self.add_argument('--unicode', action='store_boolean', default=ansi_config['unicode'], help='unicode loglevels')
|
||||
self.add_argument('--interactive', action='store_true', help='Force interactive mode even when stdout is not a tty.')
|
||||
self.add_argument('--config-file', help='The location for the configuration file')
|
||||
self.arg_only['config_file'] = ['general']
|
||||
|
||||
|
|
@ -582,6 +584,10 @@ class MILC(object):
|
|||
colorama.init()
|
||||
self.parse_args()
|
||||
self.merge_args_into_config()
|
||||
|
||||
if self.config.general.interactive:
|
||||
self.interactive = True
|
||||
|
||||
self.setup_logging()
|
||||
|
||||
return self
|
||||
|
|
|
|||
|
|
@ -20,8 +20,8 @@ def yesno(prompt, *args, default=None, **kwargs):
|
|||
If you add `--yes` and `--no` arguments to your program the user can answer questions by passing command line flags.
|
||||
|
||||
```python
|
||||
@add_argument('-y', '--yes', action='store_true', arg_only=True, help='Answer yes to all questions.')
|
||||
@add_argument('-n', '--no', action='store_true', arg_only=True, help='Answer no to all questions.')
|
||||
@cli.argument('-y', '--yes', action='store_true', arg_only=True, help='Answer yes to all questions.')
|
||||
@cli.argument('-n', '--no', action='store_true', arg_only=True, help='Answer no to all questions.')
|
||||
```
|
||||
"""
|
||||
if not args and kwargs:
|
||||
|
|
@ -33,6 +33,9 @@ def yesno(prompt, *args, default=None, **kwargs):
|
|||
if 'yes' in cli.args and cli.args.yes:
|
||||
return True
|
||||
|
||||
if not cli.interactive:
|
||||
return False
|
||||
|
||||
if default is None:
|
||||
prompt = prompt + ' [y/n] '
|
||||
elif default:
|
||||
|
|
@ -69,6 +72,9 @@ def question(prompt, *args, default=None, confirm=False, answer_type=str, valida
|
|||
if not args and kwargs:
|
||||
args = kwargs
|
||||
|
||||
if not cli.interactive:
|
||||
return default
|
||||
|
||||
if default is not None:
|
||||
prompt = '%s [%s] ' % (prompt, default)
|
||||
|
||||
|
|
@ -118,6 +124,9 @@ def choice(heading, options, *args, default=None, confirm=False, prompt='Please
|
|||
if not args and kwargs:
|
||||
args = kwargs
|
||||
|
||||
if not cli.interactive:
|
||||
return default
|
||||
|
||||
if prompt and default:
|
||||
prompt = prompt + ' [%s] ' % (default + 1,)
|
||||
|
||||
|
|
@ -145,7 +154,7 @@ def choice(heading, options, *args, default=None, confirm=False, prompt='Please
|
|||
answer = int(answer) - 1
|
||||
except Exception:
|
||||
# Normally we would log the exception here, but in the interest of clean UI we do not.
|
||||
cli.log.error('Invalid choice: %s', answer + 1)
|
||||
cli.log.error('Invalid choice: %s', answer)
|
||||
continue
|
||||
|
||||
# Validate the answer
|
||||
|
|
|
|||
29
questions
Executable file
29
questions
Executable file
|
|
@ -0,0 +1,29 @@
|
|||
#!/usr/bin/env python3
|
||||
"""Hello World implementation using MILC.
|
||||
|
||||
PYTHON_ARGCOMPLETE_OK
|
||||
"""
|
||||
from milc import cli
|
||||
from milc.questions import yesno, choice, question
|
||||
|
||||
|
||||
@cli.argument('-y', '--yes', action='store_true', arg_only=True, help='Answer yes to all questions.')
|
||||
@cli.argument('-n', '--no', action='store_true', arg_only=True, help='Answer no to all questions.')
|
||||
@cli.entrypoint('Ask some questions.')
|
||||
def main(cli):
|
||||
if yesno('Will you continue?'):
|
||||
cli.log.info('User has chosen to continue.')
|
||||
else:
|
||||
cli.log.info('User has chosen to stop.')
|
||||
|
||||
if choice('Will you stop?', ['yes', 'no']) == 'no':
|
||||
cli.log.info('User is not stopping.')
|
||||
else:
|
||||
cli.log.info('User is stopping.')
|
||||
|
||||
answer = question('Why? ')
|
||||
cli.log.info('Interesting answer: %s', answer)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
cli()
|
||||
|
|
@ -1,9 +1,9 @@
|
|||
from milc import cli
|
||||
|
||||
|
||||
def check_command(command, *args):
|
||||
def check_command(command, *args, input=None):
|
||||
cmd = [command] + list(args)
|
||||
return cli.run(cmd, combined_output=True)
|
||||
return cli.run(cmd, combined_output=True, input=input)
|
||||
|
||||
|
||||
def check_returncode(result, expected=0):
|
||||
|
|
|
|||
33
tests/test_script_questions.py
Normal file
33
tests/test_script_questions.py
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
from .common import check_command, check_returncode
|
||||
|
||||
|
||||
def test_questions():
|
||||
result = check_command('./questions')
|
||||
check_returncode(result)
|
||||
assert 'User has chosen to stop.' in result.stdout
|
||||
assert 'User is stopping.' in result.stdout
|
||||
assert 'Interesting answer: None' in result.stdout
|
||||
|
||||
|
||||
def test_questions_interactive():
|
||||
result = check_command('./questions', '--interactive', input='y\n2\nbecause\n')
|
||||
check_returncode(result)
|
||||
assert 'User has chosen to continue.' in result.stdout
|
||||
assert 'User is not stopping.' in result.stdout
|
||||
assert 'Interesting answer: because' in result.stdout
|
||||
|
||||
|
||||
def test_questions_yes():
|
||||
result = check_command('./questions', '--yes')
|
||||
check_returncode(result)
|
||||
assert 'User has chosen to continue.' in result.stdout
|
||||
assert 'User is stopping.' in result.stdout
|
||||
assert 'Interesting answer: None' in result.stdout
|
||||
|
||||
|
||||
def test_questions_no():
|
||||
result = check_command('./questions', '--no')
|
||||
check_returncode(result)
|
||||
assert 'User has chosen to stop.' in result.stdout
|
||||
assert 'User is stopping.' in result.stdout
|
||||
assert 'Interesting answer: None' in result.stdout
|
||||
Loading…
Reference in a new issue