Source code for ecdysis.cli

"""various commandline tools"""

# this is not good enough as of 2.7.13, which doesn't have
# print(flush=True)
from __future__ import print_function

import getpass
import math
import sys


[docs] class throbber: """weird logarithmic "progress bar" when a throbber object is called, will display progress using the provided "symbol" the throbber will print the symbol every time it's called until it crosses a logarithmic threshold (the "factor"), at which point the factor is increased. this is useful to display progress on large datasets that have an unknown size (so we can't guess completion time *and* we can't reasonably guess the progress/display ratio). originally from the code I wrote for the Euler project this function requires Python 3.3 at least, because it uses print(flush=True) Other progress bars include: Rich: https://rich.readthedocs.io/en/stable/progress.html tqdm: https://github.com/tqdm/tqdm progress: https://pypi.org/project/progress/ progressbar: https://pypi.org/project/progressbar/ """ def __init__(self, factor=0, stream=sys.stderr, symbol=".", fmt="{}", i=1): """build a throbber object and pass along the settings >>> throbber(stream='') throbber(i=1, factor=0, stream=, symbol=., fmt={}) """ self.i = i self.factor = factor self.stream = stream self.symbol = symbol self.fmt = fmt def __repr__(self): """nicer representation of this object mainly to ease testing of the constructor >>> throbber(factor=1,stream='',symbol='!',fmt='{s}') throbber(i=1, factor=1, stream=, symbol=!, fmt={s}) """ return "throbber(i={i}, factor={factor}, stream={stream}, symbol={symbol}, fmt={fmt})".format( **self.__dict__ ) # noqa def __call__(self, symbol=None): """increment the counter and potentially print something >>> t = throbber(stream=sys.stdout) >>> t() . >>> for i in range(1,100): t('+') ++++++++10+++++++++100 >>> # here we overrode the throbber symbol otherwise it breaks doctest """ if symbol is None: symbol = self.symbol self.i += 1 # put a dot every modulo(log10(i)) if (self.i % 10**self.factor) == 0: print(symbol, end="", file=self.stream, flush=True) # and every time we go one log10 higher, slow down the throbber if (self.i % 10 ** (self.factor + 1)) == 0: print(self.fmt.format(self.i), end="", file=self.stream, flush=True) self.factor = math.floor(float(math.log10(self.i)))
[docs] class Prompter(object): """Set of prompt utilities. This is untested. It mostly comes from Monkeysign, but was rewritten for notmuch-sync-flagged and in doing so, was significantly refactored without further tests. This could possibly be replaced with: https://github.com/prompt-toolkit/python-prompt-toolkit https://github.com/Mckinsey666/bullet """
[docs] def yes_no(self, prompt, default="y", choices=["y", "n"]): """This will show the given prompt, check if it matches the given choices, and return True if it matches the first choice provided. If some "false" string (e.g. empty string which happens when you just hit "enter") is provided, the default value (which should be a boolean) is returned. For unit testing, the input function can be overridden with input_func. >>> prompter = Prompter() >>> prompter.input = lambda x: 'y' >>> prompter.yes_no('foo') True >>> prompter.input = lambda x: 'n' >>> prompter.yes_no('foo') False >>> prompter.input = lambda x: '' >>> prompter.yes_no('foo', default='y') True >>> prompter.yes_no('foo', default='n') False """ return self.pick(prompt, default, choices) == choices[0]
[docs] def pick(self, prompt, default, choices): # prompt the user ans = self.input(prompt) # reprompt the user if choice is invalid... while ans.lower() not in choices: # ... except if the choice is "empty" (e.g. just hit "enter") # and we have a default to return (true/false) if not ans.lower() and default is not None: return default ans = self.input(prompt) # first choice is the "yes" or "truth" return ans.lower()
[docs] def acknowledge(self, prompt=None): """Just wait for the user to hit enter and return.""" if prompt is None: prompt = "press enter when ready" return self.input(prompt)
[docs] def input(self, prompt): """Wrapper around python's input function, to ease testing.""" return input(prompt)
[docs] def input_pass(self, prompt): """Input without showing the typed characters on the terminal.""" return getpass.getpass(prompt)