4
\$\begingroup\$

I implemented a class to animate waiting for processes:

from sys import stdout, stderr
from threading import Thread


def reset_string(string):
    """Returns the reset string required to delete the provided string."""

    strlen = len(string)
    return ''.join(('\b' * strlen, ' ' * strlen, '\b' * strlen))


class TTYAnimation:
    """Text animation for interactive TTY."""

    def __init__(self, message='', animation='–\\|/', end='',
                 template='{}: {}', interval=0.1, file=stdout):
        self.message = message
        self.animation = animation
        self.template = template
        self.interval = interval
        self.file = file
        self.result = end
        self._thread = None
        self._run = False

    def __enter__(self):
        """Starts animation iff in interactive Shell."""
        self.start()
        return self

    def __exit__(self, *_):
        """Stops the animation."""
        self.stop()

    def print_and_reset(self, string):
        """Prints an animation string and resets
        it after the specified interval.
        """
        print(string, flush=True, end='', file=self.file)
        sleep(self.interval)
        print(reset_string(string), flush=True, end='', file=self.file)

    def finalize(self, result):
        """Prints the terminal animation string."""
        if result is True:
            animation = str(LogLevel.SUCCESS)
        elif result is False:
            animation = str(LogLevel.ERROR)
        else:
            animation = result

        text = self.template.format(self.message, animation)
        print(text, file=self.file, flush=True)

    def run(self):
        """Runs the animation.
        Do not call this method directly,
        use start() instead.
        """
        while self._run:
            for item in self.animation:
                if not self._run:
                    break

                self.print_and_reset(self.template.format(self.message, item))

    def start(self):
        """Starts the animation."""
        if self._thread is None:
            self._run = True
            self._thread = Thread(target=self.run)
            self._thread.start()
            return True

        return False

    def stop(self, result=None):
        """Stops the animation."""
        if self._thread is None:
            return False

        self._run = False
        self._thread.join()
        self._thread = None
        self.finalize(self.result if result is None else result)
        return True

The full module can be found here.

I'd like to reduce the class' complexity while maintaining the current behaviour and get rid of threading, if possible.

Invocation example

from time import sleep

with TTYAnimation('Hang in there') as anim:
    sleep(5)
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.