Skip to content

Commit

Permalink
Added inquirer directly into Operon until pull request is accepted into
Browse files Browse the repository at this point in the history
the original project
  • Loading branch information
djf604 committed Mar 19, 2018
1 parent 4f5b9e6 commit 37d5c0a
Show file tree
Hide file tree
Showing 17 changed files with 964 additions and 2 deletions.
17 changes: 17 additions & 0 deletions LICENSE
Expand Up @@ -672,3 +672,20 @@ may consider it more useful to permit linking proprietary applications with
the library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License. But first, please read
<http://www.gnu.org/philosophy/why-not-lgpl.html>.

Inquirer
Original work Copyright 2014 Miguel Ángel García, based on Inquirer.js, by Simon Boudrias
Modified work Copyright 2018 Dominic Fitzgerald

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
documentation files (the "Software"), to deal in the Software without restriction, including without limitation the
rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit
persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions
of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
13 changes: 13 additions & 0 deletions inquirer/__init__.py
@@ -0,0 +1,13 @@
from __future__ import print_function

__version__ = '2.2.0'

try:
from .prompt import prompt
from .questions import Text, Password, Confirm, List, Checkbox, \
load_from_dict, load_from_json, Path

__all__ = ['prompt', 'Text', 'Password', 'Confirm', 'List', 'Checkbox',
'load_from_list', 'load_from_dict', 'load_from_json', 'Path']
except ImportError as e:
print("An error was found, but returning just with the version: %s" % e)
26 changes: 26 additions & 0 deletions inquirer/errors.py
@@ -0,0 +1,26 @@
class InquirerError(Exception):
pass


class ValidationError(InquirerError):
def __init__(self, value, *args, **kwargs):
super(ValidationError, self).__init__(*args, **kwargs)
self.value = value


class UnknownQuestionTypeError(InquirerError):
pass


class Aborted(InquirerError):
pass


class EndOfInput(InquirerError):
def __init__(self, selection, *args, **kwargs):
super(EndOfInput, self).__init__(*args, **kwargs)
self.selection = selection


class ThemeError(AttributeError):
pass
22 changes: 22 additions & 0 deletions inquirer/events.py
@@ -0,0 +1,22 @@
import readchar


class Event(object):
pass


class KeyPressed(Event):
def __init__(self, value):
self.value = value


class Repaint(Event):
pass


class KeyEventGenerator(object):
def __init__(self, key_generator=None):
self._key_gen = key_generator or readchar.readkey

def next(self):
return KeyPressed(self._key_gen())
21 changes: 21 additions & 0 deletions inquirer/prompt.py
@@ -0,0 +1,21 @@
# -*- coding: utf-8 -*-

from .render.console import ConsoleRender
from . import themes


def prompt(questions, render=None, answers=None,
theme=themes.Default(), raise_keyboard_interrupt=False):
render = render or ConsoleRender(theme=theme)
answers = answers or {}

try:
for question in questions:
answers[question.name] = render.render(question, answers)
return answers
except KeyboardInterrupt:
if raise_keyboard_interrupt:
raise
print('')
print('Cancelled by user')
print('')
183 changes: 183 additions & 0 deletions inquirer/questions.py
@@ -0,0 +1,183 @@
# -*- coding: utf-8 -*-
"""
Module that implements the questions types
"""

import json

from . import errors


def question_factory(kind, *args, **kwargs):
for clazz in (Text, Password, Confirm, List, Checkbox, Path):
if clazz.kind == kind:
return clazz(*args, **kwargs)
raise errors.UnknownQuestionTypeError()


def load_from_dict(question_dict):
"""
Load one question from a dict.
It requires the keys 'name' and 'kind'.
:return: The Question object with associated data.
:return type: Question
"""
return question_factory(**question_dict)


def load_from_list(question_list):
"""
Load a list of questions from a list of dicts.
It requires the keys 'name' and 'kind' for each dict.
:return: A list of Question objects with associated data.
:return type: List
"""
return [load_from_dict(q) for q in question_list]


def load_from_json(question_json):
"""
Load Questions from a JSON string.
:return: A list of Question objects with associated data if the JSON
contains a list or a Question if the JSON contains a dict.
:return type: List or Dict
"""
data = json.loads(question_json)
if isinstance(data, list):
return load_from_list(data)
if isinstance(data, dict):
return load_from_dict(data)
raise TypeError(
'Json contained a %s variable when a dict or list was expected',
type(data))


class TaggedValue(object):
def __init__(self, label, value):
self.label = label
self.value = value

def __str__(self):
return self.label

def __repr__(self):
return self.value

def __eq__(self, other):
if isinstance(other, TaggedValue):
return self.value == other.value
return self.value == other

def __ne__(self, other):
return not self.__eq__(other)


class Question(object):
kind = 'base question'

def __init__(self,
name,
message='',
choices=None,
default=None,
ignore=False,
validate=True,
show_default=False):
self.name = name
self._message = message
self._choices = choices or []
self._default = default
self._ignore = ignore
self._validate = validate
self.answers = {}
self.show_default = show_default

@property
def ignore(self):
return bool(self._solve(self._ignore))

@property
def message(self):
return self._solve(self._message)

@property
def default(self):
return self.answers.get(self.name) or self._solve(self._default)

@property
def choices_generator(self):
for choice in self._solve(self._choices):
yield (
TaggedValue(*choice)
if isinstance(choice, tuple) and len(choice) == 2
else choice
)

@property
def choices(self):
return list(self.choices_generator)

def validate(self, current):
try:
if self._solve(self._validate, current):
return
except Exception:
pass
raise errors.ValidationError(current)

def _solve(self, prop, *args, **kwargs):
if callable(prop):
return prop(self.answers, *args, **kwargs)
if isinstance(prop, str):
return prop.format(**self.answers)
return prop


class Text(Question):
kind = 'text'


class Password(Question):
kind = 'password'


class Confirm(Question):
kind = 'confirm'

def __init__(self, name, message='', default=False, **kwargs):
super(Confirm, self).__init__(name, message, default=default, **kwargs)


class List(Question):
kind = 'list'

def __init__(self,
name,
message='',
choices=None,
default=None,
ignore=False,
validate=True,
carousel=False):

super(List, self).__init__(
name, message, choices,
default, ignore, validate
)
self.carousel = carousel


class Checkbox(Question):
kind = 'checkbox'


class Path(Question):
kind = 'path'

def __init__(self,
name,
message='',
midtoken_completion=True,
**kwargs):
super(Path, self).__init__(name, message, **kwargs)
self.midtoken_completion = midtoken_completion
10 changes: 10 additions & 0 deletions inquirer/render/__init__.py
@@ -0,0 +1,10 @@
# -*- coding: utf-8 -*-
from .console import ConsoleRender


class Render(object):
def __init__(self, impl=ConsoleRender):
self._impl = impl

def render(self, question, answers):
return self._impl.render(question, answers)

0 comments on commit 37d5c0a

Please sign in to comment.