You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
Enso-Bot/venv/Lib/site-packages/dsdev_utils/terminal.py

285 lines
8.1 KiB
Python

4 years ago
# ------------------------------------------------------------------------------
# The MIT License (MIT)
#
# Copyright (c) 2014-2019 Digital Sapphire
#
# 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.
# ------------------------------------------------------------------------------
from __future__ import print_function
import logging
try:
import msvcrt
except ImportError:
msvcrt = None
import locale
import optparse
import os
import platform
import shlex
import struct
import subprocess
import sys
try:
import termios
except ImportError:
termios = None
try:
import tty
except ImportError:
tty = None
import six
log = logging.getLogger(__name__)
def print_to_console(text):
enc = locale.getdefaultlocale()[1] or "utf-8"
try:
print(text.encode(enc, errors="backslashreplace"))
except (LookupError, UnicodeEncodeError):
# Unknown encoding or encoding problem. Fallback to ascii
print(text.encode("ascii", errors="backslashreplace"))
def terminal_formatter():
max_width = 80
max_help_position = 80
# No need to wrap help messages if we're on a wide console
columns = get_terminal_size()[0]
if columns:
max_width = columns
fmt = optparse.IndentedHelpFormatter(width=max_width,
max_help_position=max_help_position)
return fmt
# get width and height of console
# works on linux, os x, windows, cygwin(windows)
# originally retrieved from:
# http://stackoverflow.com/questions/
# 566746/how-to-get-console-window-width-in-python
def get_terminal_size():
current_os = platform.system()
tuple_xy = None
if current_os == u'Windows':
tuple_xy = _get_terminal_size_windows()
if tuple_xy is None:
tuple_xy = _get_terminal_size_tput()
# needed for window's python in cygwin's xterm!
if current_os in [u'Linux', u'Darwin'] or current_os.startswith('CYGWIN'):
tuple_xy = _get_terminal_size_linux()
if tuple_xy is None:
log.debug(u"default")
tuple_xy = (80, 25) # default value
return tuple_xy
def _get_terminal_size_windows():
try:
from ctypes import windll, create_string_buffer
# stdin handle is -10
# stdout handle is -11
# stderr handle is -12
h = windll.kernel32.GetStdHandle(-12)
csbi = create_string_buffer(22)
res = windll.kernel32.GetConsoleScreenBufferInfo(h, csbi)
if res:
(bufx, bufy, curx, cury, wattr,
left, top, right, bottom,
maxx, maxy) = struct.unpack("hhhhHhhhhhh", csbi.raw)
sizex = right - left + 1
sizey = bottom - top + 1
return sizex, sizey
except:
pass
def _get_terminal_size_tput():
# get terminal width
# http://stackoverflow.com/questions/263890/
# how-do-i-find-the-width-height-of-a-terminal-window
try:
cols = int(subprocess.check_call(shlex.split('tput cols')))
rows = int(subprocess.check_call(shlex.split('tput lines')))
return (cols, rows)
except:
pass
def _get_terminal_size_linux():
def ioctl_GWINSZ(fd):
try:
import fcntl
# Is this required
# import termios
cr = struct.unpack('hh',
fcntl.ioctl(fd, termios.TIOCGWINSZ, '1234'))
return cr
except:
pass
cr = ioctl_GWINSZ(0) or ioctl_GWINSZ(1) or ioctl_GWINSZ(2)
if not cr:
try:
fd = os.open(os.ctermid(), os.O_RDONLY)
cr = ioctl_GWINSZ(fd)
os.close(fd)
except:
pass
if not cr:
try:
cr = (os.environ['LINES'], os.environ['COLUMNS'])
except:
return None
return int(cr[1]), int(cr[0])
# Gets a single character form standard input. Does not echo to the screen
class GetCh:
def __init__(self):
if sys.platform == u'win32':
self.impl = _GetchWindows()
else:
self.impl = _GetchUnix()
def __call__(self):
return self.impl()
class _GetchUnix:
def __init__(self):
pass
def __call__(self):
pass
fd = sys.stdin.fileno()
old_settings = termios.tcgetattr(fd)
try:
tty.setraw(sys.stdin.fileno())
ch = sys.stdin.read(1)
finally:
termios.tcsetattr(fd, termios.TCSADRAIN, old_settings)
return ch
class _GetchWindows:
def __init__(self):
pass
def __call__(self):
return msvcrt.getch()
def ask_yes_no(question, default='no', answer=None):
u"""Will ask a question and keeps prompting until
answered.
Args:
question (str): Question to ask end user
default (str): Default answer if user just press enter at prompt
answer (str): Used for testing
Returns:
(bool) Meaning:
True - Answer is yes
False - Answer is no
"""
default = default.lower()
yes = [u'yes', u'ye', u'y']
no = [u'no', u'n']
if default in no:
help_ = u'[N/y]?'
default = False
else:
default = True
help_ = u'[Y/n]?'
while 1:
display = question + '\n' + help_
if answer is None:
log.debug(u'Under None')
answer = six.moves.input(display)
answer = answer.lower()
if answer == u'':
log.debug(u'Under blank')
return default
if answer in yes:
log.debug(u'Must be true')
return True
elif answer in no:
log.debug(u'Must be false')
return False
else:
sys.stdout.write(u'Please answer yes or no only!\n\n')
sys.stdout.flush()
answer = None
six.moves.input(u'Press enter to continue')
sys.stdout.write('\n\n\n\n\n')
sys.stdout.flush()
def get_correct_answer(question, default=None, required=False,
answer=None, is_answer_correct=None):
u"""Ask user a question and confirm answer
Args:
question (str): Question to ask user
default (str): Default answer if no input from user
required (str): Require user to input answer
answer (str): Used for testing
is_answer_correct (str): Used for testing
"""
while 1:
if default is None:
msg = u' - No Default Available'
else:
msg = (u'\n[DEFAULT] -> {}\nPress Enter To '
u'Use Default'.format(default))
prompt = question + msg + u'\n--> '
if answer is None:
answer = six.moves.input(prompt)
if answer == '' and required and default is not None:
print(u'You have to enter a value\n\n')
six.moves.input(u'Press enter to continue')
print(u'\n\n')
answer = None
continue
if answer == u'' and default is not None:
answer = default
_ans = ask_yes_no(u'You entered {}, is this '
u'correct?'.format(answer),
answer=is_answer_correct)
if _ans:
return answer
else:
answer = None