# -*- encoding: utf-8 -*-
# PKGBUILDer v4.3.2
# An AUR helper (and library) in Python 3.
# Copyright © 2011-2023, Chris Warrick.
# See /LICENSE for licensing information.
"""
The User Interface.
:Copyright: © 2011-2023, Chris Warrick.
:License: BSD (see /LICENSE).
"""
import sys
import time
import threading
import textwrap
import shutil
__all__ = ('get_termwidth', 'hanging_indent', 'mlist',
'Progress', 'Throbber', 'ProgressThrobber')
[docs]def get_termwidth(default=None):
"""Get the width of this terminal.
.. versionadded:: 3.3.0
.. versionchanged:: 4.2.9
"""
return shutil.get_terminal_size((default, default)).columns
[docs]def hanging_indent(text, intro, termwidth=None, change_spaces=True,
introwidth=None):
"""Produce text with a hanging indent.
.. versionadded:: 3.3.0
.. versionchanged:: 4.0.0
"""
if termwidth is None:
termwidth = get_termwidth(9001)
if introwidth is None:
introwidth = len(intro)
nowrap = intro + text
if intro:
wrapv = textwrap.wrap(nowrap, termwidth,
break_on_hyphens=False)
else:
wrapv = textwrap.wrap(nowrap, termwidth - introwidth,
break_on_hyphens=False)
wrap0 = wrapv[0]
wraprest = textwrap.wrap('\n'.join(wrapv[1:]), termwidth -
introwidth,
break_on_hyphens=False)
if change_spaces:
wraprest = [i.replace(' ', ' ').replace(' ', ' ') for i
in wraprest]
buf = wrap0
for i in wraprest:
buf += '\n' + introwidth * ' ' + i
return buf
[docs]def mlist(items, sep=' ', change_spaces=True, termwidth=None, indentwidth=17):
"""Output a list of strings, complete with a hanging indent.
.. versionadded:: 3.3.0
.. versionchanged:: 4.0.0
"""
if termwidth is None:
termwidth = get_termwidth(9001)
if items:
if sep == '\n':
buf = [hanging_indent(items[0], '', termwidth, change_spaces,
indentwidth)]
for i in items[1:]:
buf.append(hanging_indent(i, indentwidth * ' ', termwidth,
change_spaces))
return '\n'.join(buf)
else:
return hanging_indent(sep.join(items), '', termwidth,
change_spaces, indentwidth)
else:
return 'None'
[docs]class Progress(object):
"""A static progress indicator with numbers.
Usage::
pm = Progress(total=2)
pm.msg('Doing step 1...')
step1()
pm.msg('Doing step 2...')
step2()
"""
current = 0
total = 1
_pml = 0
def __init__(self, total=1):
"""Initialize a Progress message."""
self.total = total
[docs] def msg(self, msg, single=False):
"""Print a progress message."""
self.current += 1
ln = len(str(self.total))
sys.stdout.write('\r' + ((ln * 2 + 4 + self._pml) * ' '))
self._pml = len(msg)
sys.stdout.write('\r')
sys.stdout.flush()
sys.stdout.write(('({0:>' + str(ln) + '}/{1}) ').format(self.current,
self.total))
sys.stdout.write(msg)
sys.stdout.write('\r')
sys.stdout.flush()
if single:
print()
if self.current == self.total:
self.total = 0
self.current = 0
[docs]class Throbber(object):
"""A nice animated throbber.
Usage::
with Throbber('Doing important stuff...'):
dostuff()
"""
throb = False
states = ('|', '/', '-', '\\')
_tt = None
def __init__(self, msg, finalthrob='*', printback=True):
"""Initialize."""
self.msg = msg
self.finalthrob = finalthrob
self.printback = printback
def __enter__(self):
"""Run the throbber in a thread."""
self._tt = threading.Thread(target=self._throb, args=(
self.msg, self.finalthrob, self.printback))
self._tt.start()
return self
def __exit__(self, *args, **kwargs):
"""Clean stuff up."""
self.throb = False
while self.throbber_alive:
time.sleep(0.1)
def _throb(self, msg, finalthrob='*', printback=True):
"""Display a throbber."""
self.throb = True
i = 0
while self.throb:
sys.stdout.write('\r({0}) {1}'.format(self.states[i], msg))
sys.stdout.flush()
time.sleep(0.1)
i += 1
if i == len(self.states):
i = 0
if not self.throb and printback:
sys.stdout.write('\r({0}) {1}'.format(finalthrob, msg))
sys.stdout.flush()
time.sleep(0.1)
print()
@property
def throbber_alive(self):
"""Check the status of a throbber."""
if self._tt:
return self._tt.is_alive()
else:
return False
[docs]class ProgressThrobber(Progress, Throbber):
"""An animated progress throbber.
Similar to Progress, but the / is animated.
Usage::
with ProgressThrobber('Working...', total=2) as pt:
dostuff()
pt.bump('Cleaning up...')
cleanup()
"""
current = 0
finalthrob = '/'
printback = True
def __init__(self, msg, total=1):
"""Initialize a ProgressThrobber message."""
self.total = total
self.ln = len(str(self.total))
self.bump(msg)
def _throb(self, msg, finalthrob='/', printback=True):
"""Display a throbber."""
self.throb = True
i = 0
while self.throb:
sys.stdout.write(('\r({0:>' + str(self.ln) +
'}{1}{2}) {3}').format(self.current,
self.states[i],
self.total, self.msg))
sys.stdout.write('\r')
sys.stdout.flush()
time.sleep(0.1)
i += 1
if i == len(self.states):
i = 0
sys.stdout.write('\r({0}{1}{2}) {3}'.format(self.current,
self.finalthrob,
self.total, self.msg))
sys.stdout.flush()
time.sleep(0.1)
if self.printback:
print()
[docs] def bump(self, msg):
"""Change the displayed message."""
sys.stdout.write('\r' + ((self.ln * 2 + 4 + self._pml) * ' '))
self._pml = len(msg)
self.current += 1
self.msg = msg