]>
Commit | Line | Data |
---|---|---|
b5ae35ee | 1 | import functools |
bd50a52b | 2 | from threading import Lock |
d1d5c08f | 3 | from .utils import supports_terminal_sequences, TERMINAL_SEQUENCES, write_string |
819e0531 | 4 | |
bd50a52b | 5 | |
819e0531 | 6 | class MultilinePrinterBase: |
7 | def __init__(self, stream=None, lines=1): | |
8 | self.stream = stream | |
9 | self.maximum = lines - 1 | |
bd50a52b | 10 | |
bd50a52b THD |
11 | def __enter__(self): |
12 | return self | |
13 | ||
14 | def __exit__(self, *args): | |
15 | self.end() | |
16 | ||
17 | def print_at_line(self, text, pos): | |
18 | pass | |
19 | ||
20 | def end(self): | |
21 | pass | |
22 | ||
819e0531 | 23 | def _add_line_number(self, text, line): |
24 | if self.maximum: | |
25 | return f'{line + 1}: {text}' | |
26 | return text | |
bd50a52b | 27 | |
d1d5c08f | 28 | def write(self, *text): |
29 | write_string(''.join(text), self.stream) | |
30 | ||
bd50a52b | 31 | |
819e0531 | 32 | class QuietMultilinePrinter(MultilinePrinterBase): |
33 | pass | |
bd50a52b | 34 | |
bd50a52b | 35 | |
819e0531 | 36 | class MultilineLogger(MultilinePrinterBase): |
d1d5c08f | 37 | def write(self, *text): |
38 | self.stream.debug(''.join(text)) | |
39 | ||
819e0531 | 40 | def print_at_line(self, text, pos): |
41 | # stream is the logger object, not an actual stream | |
d1d5c08f | 42 | self.write(self._add_line_number(text, pos)) |
bd50a52b | 43 | |
bd50a52b | 44 | |
819e0531 | 45 | class BreaklineStatusPrinter(MultilinePrinterBase): |
46 | def print_at_line(self, text, pos): | |
d1d5c08f | 47 | self.write(self._add_line_number(text, pos), '\n') |
bd50a52b | 48 | |
819e0531 | 49 | |
50 | class MultilinePrinter(MultilinePrinterBase): | |
51 | def __init__(self, stream=None, lines=1, preserve_output=True): | |
52 | super().__init__(stream, lines) | |
53 | self.preserve_output = preserve_output | |
54 | self._lastline = self._lastlength = 0 | |
55 | self._movelock = Lock() | |
56 | self._HAVE_FULLCAP = supports_terminal_sequences(self.stream) | |
57 | ||
58 | def lock(func): | |
b5ae35ee | 59 | @functools.wraps(func) |
819e0531 | 60 | def wrapper(self, *args, **kwargs): |
61 | with self._movelock: | |
62 | return func(self, *args, **kwargs) | |
63 | return wrapper | |
bd50a52b THD |
64 | |
65 | def _move_cursor(self, dest): | |
819e0531 | 66 | current = min(self._lastline, self.maximum) |
d1d5c08f | 67 | yield '\r' |
819e0531 | 68 | distance = dest - current |
69 | if distance < 0: | |
d1d5c08f | 70 | yield TERMINAL_SEQUENCES['UP'] * -distance |
819e0531 | 71 | elif distance > 0: |
d1d5c08f | 72 | yield TERMINAL_SEQUENCES['DOWN'] * distance |
819e0531 | 73 | self._lastline = dest |
74 | ||
75 | @lock | |
76 | def print_at_line(self, text, pos): | |
77 | if self._HAVE_FULLCAP: | |
d1d5c08f | 78 | self.write(*self._move_cursor(pos), TERMINAL_SEQUENCES['ERASE_LINE'], text) |
bd50a52b | 79 | |
819e0531 | 80 | text = self._add_line_number(text, pos) |
81 | textlen = len(text) | |
82 | if self._lastline == pos: | |
83 | # move cursor at the start of progress when writing to same line | |
d1d5c08f | 84 | prefix = '\r' |
819e0531 | 85 | if self._lastlength > textlen: |
86 | text += ' ' * (self._lastlength - textlen) | |
87 | self._lastlength = textlen | |
88 | else: | |
89 | # otherwise, break the line | |
d1d5c08f | 90 | prefix = '\n' |
819e0531 | 91 | self._lastlength = textlen |
d1d5c08f | 92 | self.write(prefix, text) |
819e0531 | 93 | self._lastline = pos |
bd50a52b | 94 | |
819e0531 | 95 | @lock |
bd50a52b | 96 | def end(self): |
819e0531 | 97 | # move cursor to the end of the last line, and write line break |
98 | # so that other to_screen calls can precede | |
d1d5c08f | 99 | text = self._move_cursor(self.maximum) if self._HAVE_FULLCAP else [] |
819e0531 | 100 | if self.preserve_output: |
d1d5c08f | 101 | self.write(*text, '\n') |
819e0531 | 102 | return |
bd50a52b | 103 | |
819e0531 | 104 | if self._HAVE_FULLCAP: |
d1d5c08f | 105 | self.write( |
106 | *text, TERMINAL_SEQUENCES['ERASE_LINE'], | |
107 | f'{TERMINAL_SEQUENCES["UP"]}{TERMINAL_SEQUENCES["ERASE_LINE"]}' * self.maximum) | |
819e0531 | 108 | else: |
d1d5c08f | 109 | self.write(*text, ' ' * self._lastlength) |