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

[v2] Initial groundwork for wizard improvements #5728

Merged
merged 7 commits into from Nov 18, 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
2 changes: 1 addition & 1 deletion awscli/customizations/configure/sso.py
Expand Up @@ -28,7 +28,7 @@
from awscli.customizations.commands import BasicCommand
from awscli.customizations.configure import profile_to_section
from awscli.customizations.configure.writer import ConfigFileWriter
from awscli.customizations.wizard.selectmenu import select_menu
from awscli.customizations.wizard.ui.selectmenu import select_menu
from awscli.customizations.sso.utils import do_sso_login
from awscli.formatter import CLI_OUTPUT_FORMATS

Expand Down
30 changes: 30 additions & 0 deletions awscli/customizations/wizard/app.py
@@ -0,0 +1,30 @@
# Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"). You
# may not use this file except in compliance with the License. A copy of
# the License is located at
#
# http://aws.amazon.com/apache2.0/
#
# or in the "license" file accompanying this file. This file is
# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
# ANY KIND, either express or implied. See the License for the specific
# language governing permissions and limitations under the License.
from prompt_toolkit.application import Application
from prompt_toolkit.key_binding import KeyBindings

from awscli.customizations.wizard.ui.keybindings import get_default_keybindings
from awscli.customizations.wizard.ui.layout import WizardLayoutFactory
from awscli.customizations.wizard.ui.style import get_default_style


def create_wizard_app(definition):
layout_factory = WizardLayoutFactory()
app = Application(
key_bindings=get_default_keybindings(),
style=get_default_style(),
layout=layout_factory.create_wizard_layout(definition),
full_screen=True,
)
app.values = {}
return app
11 changes: 5 additions & 6 deletions awscli/customizations/wizard/devcommands.py
Expand Up @@ -12,7 +12,7 @@
# language governing permissions and limitations under the License.
import ruamel.yaml as yaml
from awscli.customizations.commands import BasicCommand
from awscli.customizations.wizard import factory
from awscli.customizations.wizard.app import create_wizard_app


def register_dev_commands(event_handlers):
Expand All @@ -21,10 +21,8 @@ def register_dev_commands(event_handlers):


def create_default_wizard_dev_runner(session):
runner = factory.create_default_wizard_runner(session)
return WizardDevRunner(
wizard_loader=WizardLoader(),
wizard_runner=runner
)


Expand All @@ -35,14 +33,15 @@ def load(self, contents):


class WizardDevRunner(object):
def __init__(self, wizard_loader, wizard_runner):
def __init__(self, wizard_loader):
self._wizard_loader = wizard_loader
self._wizard_runner = wizard_runner

def run_wizard(self, wizard_contents):
"""Run a single wizard given the contents as a string."""
loaded = self._wizard_loader.load(wizard_contents)
self._wizard_runner.run(loaded)
app = create_wizard_app(loaded)
app.run()
print(f'Collected values: {app.values}')


class WizardDev(BasicCommand):
Expand Down
Expand Up @@ -16,7 +16,7 @@
import prompt_toolkit
from prompt_toolkit.completion import Completer, Completion

from awscli.customizations.wizard import selectmenu
from awscli.customizations.wizard.ui import selectmenu


class Prompter(object):
Expand Down
38 changes: 38 additions & 0 deletions awscli/customizations/wizard/ui/keybindings.py
@@ -0,0 +1,38 @@
# Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"). You
# may not use this file except in compliance with the License. A copy of
# the License is located at
#
# http://aws.amazon.com/apache2.0/
#
# or in the "license" file accompanying this file. This file is
# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
# ANY KIND, either express or implied. See the License for the specific
# language governing permissions and limitations under the License.
from prompt_toolkit.key_binding import KeyBindings


def get_default_keybindings():
kb = KeyBindings()

@kb.add('c-c')
def exit_(event):
event.app.exit()

def submit_current_answer(event):
current_buffer = event.app.current_buffer
event.app.values[current_buffer.name] = current_buffer.text

@kb.add('tab')
@kb.add('enter')
def next_prompt(event):
submit_current_answer(event)
event.app.layout.focus_next()

@kb.add('s-tab')
def last_prompt(event):
submit_current_answer(event)
event.app.layout.focus_last()

return kb
50 changes: 50 additions & 0 deletions awscli/customizations/wizard/ui/layout.py
@@ -0,0 +1,50 @@
# Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"). You
# may not use this file except in compliance with the License. A copy of
# the License is located at
#
# http://aws.amazon.com/apache2.0/
#
# or in the "license" file accompanying this file. This file is
# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
# ANY KIND, either express or implied. See the License for the specific
# language governing permissions and limitations under the License.
from prompt_toolkit.layout import Layout
from prompt_toolkit.layout.containers import WindowAlign, HSplit
from prompt_toolkit.widgets import Label

from awscli.customizations.wizard.ui.prompt import WizardPrompt


class WizardLayoutFactory:
def create_wizard_layout(self, defintion):
return Layout(
container=HSplit(
[
self._create_title(defintion),
self._create_all_prompts(defintion)
],
padding=1,
)
)

def _create_title(self, definition):
title = Label(f'{definition["title"]}', style='class:wizard.title')
title.window.align = WindowAlign.CENTER
return title

def _create_all_prompts(self, definition):
prompts = []
for step_definition in definition['plan'].values():
prompts.extend(
self._create_prompts_from_step_definition(step_definition)
)
return HSplit(prompts, padding=0)

def _create_prompts_from_step_definition(self, step_definition):
prompts = []
for value_name, value_definition in step_definition['values'].items():
if value_definition['type'] == 'prompt':
prompts.append(WizardPrompt(value_name, value_definition))
return prompts
93 changes: 93 additions & 0 deletions awscli/customizations/wizard/ui/prompt.py
@@ -0,0 +1,93 @@
# Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"). You
# may not use this file except in compliance with the License. A copy of
# the License is located at
#
# http://aws.amazon.com/apache2.0/
#
# or in the "license" file accompanying this file. This file is
# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
# ANY KIND, either express or implied. See the License for the specific
# language governing permissions and limitations under the License.
from prompt_toolkit.application import get_app
from prompt_toolkit.buffer import Buffer
from prompt_toolkit.document import Document
from prompt_toolkit.layout.containers import Window, VSplit, Dimension
from prompt_toolkit.layout.controls import BufferControl


class WizardPrompt:
def __init__(self, value_name, value_definition):
self._value_name = value_name
self._value_definition = value_definition
self.container = self._get_container()

def _get_container(self):
return VSplit(
[
WizardPromptDescription(
self._value_name,
self._value_definition['description']
),
WizardPromptAnswer(self._value_name)
]
)

def __pt_container__(self):
return self.container


class WizardPromptDescription:
def __init__(self, value_name, value_description):
self._value_name = value_name
self._value_description = value_description
self.container = self._get_container()

def _get_container(self):
content = f'{self._value_description}:'
buffer = Buffer(
document=Document(content),
read_only=True
)
return Window(
content=BufferControl(
buffer=buffer, focusable=False
),
style=self._get_style,
width=Dimension.exact(len(content) + 1),
dont_extend_height=True,
)

def _get_style(self):
if get_app().layout.has_focus(self._value_name):
return 'class:wizard.prompt.description.current'
else:
return 'class:wizard.prompt.description'

def __pt_container__(self):
return self.container


class WizardPromptAnswer:
def __init__(self, value_name):
self._value_name = value_name
self.container = self._get_answer_container()

def _get_answer_container(self):
return Window(
content=BufferControl(
buffer=Buffer(name=self._value_name)
),
style=self._get_style,
dont_extend_height=True,
)

def _get_style(self):
if get_app().layout.has_focus(self._value_name):
return 'class:wizard.prompt.answer.current'
else:
return 'class:wizard.prompt.answer'

def __pt_container__(self):
return self.container
26 changes: 26 additions & 0 deletions awscli/customizations/wizard/ui/style.py
@@ -0,0 +1,26 @@
# Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"). You
# may not use this file except in compliance with the License. A copy of
# the License is located at
#
# http://aws.amazon.com/apache2.0/
#
# or in the "license" file accompanying this file. This file is
# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
# ANY KIND, either express or implied. See the License for the specific
# language governing permissions and limitations under the License.
from prompt_toolkit.styles import Style


def get_default_style():
return Style(
[
('wizard', ''),
('wizard.title', 'underline bold'),
('wizard.prompt.description', 'bold'),
('wizard.prompt.description.current', 'white'),
('wizard.prompt.answer', ''),
('wizard.prompt.answer.current', 'white'),
]
)