omlish 0.0.0.dev186__py3-none-any.whl → 0.0.0.dev188__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
omlish/__about__.py CHANGED
@@ -1,5 +1,5 @@
1
- __version__ = '0.0.0.dev186'
2
- __revision__ = '2e86339e0b56687c95088fc3239b443df721c8ef'
1
+ __version__ = '0.0.0.dev188'
2
+ __revision__ = '5fdaec4377bb26043e0d2e86d311ce717e41ca0f'
3
3
 
4
4
 
5
5
  #
omlish/logs/color.py CHANGED
@@ -2,15 +2,15 @@
2
2
  import logging
3
3
  import typing as ta
4
4
 
5
- from .. import term
5
+ from ..term import codes as tc
6
6
  from .standard import StandardLogFormatter
7
7
 
8
8
 
9
9
  class ColorLogFormatter(StandardLogFormatter):
10
- LEVEL_COLORS: ta.Mapping[int, term.SGRs.FG] = {
11
- logging.WARNING: term.SGRs.FG.BRIGHT_YELLOW,
12
- logging.ERROR: term.SGRs.FG.BRIGHT_RED,
13
- logging.CRITICAL: term.SGRs.FG.BRIGHT_RED,
10
+ LEVEL_COLORS: ta.Mapping[int, tc.SGRs.FG] = {
11
+ logging.WARNING: tc.SGRs.FG.BRIGHT_YELLOW,
12
+ logging.ERROR: tc.SGRs.FG.BRIGHT_RED,
13
+ logging.CRITICAL: tc.SGRs.FG.BRIGHT_RED,
14
14
  }
15
15
 
16
16
  def formatMessage(self, record):
@@ -20,5 +20,5 @@ class ColorLogFormatter(StandardLogFormatter):
20
20
  except KeyError:
21
21
  pass
22
22
  else:
23
- buf = term.SGR(c) + buf + term.SGR(term.SGRs.RESET)
23
+ buf = tc.SGR(c) + buf + tc.SGR(tc.SGRs.RESET)
24
24
  return buf
File without changes
@@ -1,13 +1,8 @@
1
1
  import enum
2
2
  import re
3
- import sys
4
- import time
5
3
  import typing as ta
6
4
 
7
- from . import lang
8
-
9
-
10
- T = ta.TypeVar('T')
5
+ from .. import lang
11
6
 
12
7
 
13
8
  ##
@@ -233,139 +228,6 @@ BG24_RGB = ControlSequence(
233
228
  ##
234
229
 
235
230
 
236
- class ProgressBar:
237
- """
238
- TODO:
239
- - ProgressBarRenderer
240
- - right-justify
241
- - ProgressBarGroup
242
- - animate
243
- """
244
-
245
- def __init__(
246
- self,
247
- total: int | None = None,
248
- *,
249
- length: int = 40,
250
- interval: float = .2,
251
- start_time: float | None = None,
252
- out: ta.TextIO | None = None,
253
- ) -> None:
254
- super().__init__()
255
-
256
- self._total = total
257
- self._length = length
258
- self._interval = interval
259
- if start_time is None:
260
- start_time = time.time()
261
- self._start_time = start_time
262
- if out is None:
263
- out = sys.stdout
264
- self._out = out
265
-
266
- self._i = 0
267
- self._elapsed = 0.
268
- self._last_print = 0.
269
-
270
- def render(
271
- self,
272
- *,
273
- complete: bool = False,
274
- ) -> str:
275
- iter_per_sec = self._i / self._elapsed if self._elapsed > 0 else 0
276
-
277
- if self._total is not None:
278
- remaining = (self._total - self._i) / iter_per_sec if iter_per_sec > 0 else 0
279
- done = int(self._length * self._i / self._total)
280
-
281
- bar = f'[{"█" * done}{"." * (self._length - done)}]'
282
- info_parts = [
283
- f'{self._i}/{self._total}',
284
- f'{iter_per_sec:.2f} it/s',
285
- f'{self._elapsed:.2f}s elapsed',
286
- f'{remaining:.2f}s left',
287
- ]
288
-
289
- else:
290
- bar = f'[{("█" if complete else "?") * self._length}]'
291
- info_parts = [
292
- f'{self._i}',
293
- f'{iter_per_sec:.2f} it/s',
294
- f'{self._elapsed:.2f}s elapsed',
295
- ]
296
-
297
- info = ' | '.join(info_parts)
298
- return f'{bar} {info}'
299
-
300
- def print(
301
- self,
302
- *,
303
- now: float | None = None,
304
- **kwargs: ta.Any,
305
- ) -> None:
306
- if now is None:
307
- now = time.time()
308
-
309
- line = self.render(**kwargs)
310
- self._out.write(f'\033[2K\033[G{line}')
311
- self._out.flush()
312
-
313
- self._last_print = now
314
-
315
- def update(
316
- self,
317
- n: int = 1,
318
- *,
319
- now: float | None = None,
320
- silent: bool = False,
321
- ) -> None:
322
- if now is None:
323
- now = time.time()
324
-
325
- self._i += n
326
- self._elapsed = now - self._start_time
327
-
328
- if not silent:
329
- if now - self._last_print >= self._interval:
330
- self.print(now=now)
331
-
332
-
333
- def progress_bar(
334
- seq: ta.Iterable[T],
335
- *,
336
- no_tty_check: bool = False,
337
- total: int | None = None,
338
- out: ta.TextIO | None = None,
339
- **kwargs: ta.Any,
340
- ) -> ta.Generator[T, None, None]:
341
- if out is None:
342
- out = sys.stdout
343
-
344
- if not no_tty_check and not out.isatty():
345
- yield from seq
346
- return
347
-
348
- if total is None:
349
- if isinstance(seq, ta.Sized):
350
- total = len(seq)
351
-
352
- pb = ProgressBar(
353
- total=total,
354
- out=out,
355
- **kwargs,
356
- )
357
-
358
- for item in seq:
359
- pb.update()
360
- yield item
361
-
362
- pb.print(complete=True)
363
- out.write('\n')
364
-
365
-
366
- ##
367
-
368
-
369
231
  def main() -> None:
370
232
  import sys
371
233
 
@@ -0,0 +1,139 @@
1
+ import sys
2
+ import time
3
+ import typing as ta
4
+
5
+
6
+ T = ta.TypeVar('T')
7
+
8
+
9
+ ##
10
+
11
+
12
+ class ProgressBar:
13
+ """
14
+ TODO:
15
+ - ProgressBarRenderer
16
+ - right-justify
17
+ - ProgressBarGroup
18
+ - animate
19
+ """
20
+
21
+ def __init__(
22
+ self,
23
+ total: int | None = None,
24
+ *,
25
+ length: int = 40,
26
+ interval: float = .2,
27
+ start_time: float | None = None,
28
+ out: ta.TextIO | None = None,
29
+ ) -> None:
30
+ super().__init__()
31
+
32
+ self._total = total
33
+ self._length = length
34
+ self._interval = interval
35
+ if start_time is None:
36
+ start_time = time.time()
37
+ self._start_time = start_time
38
+ if out is None:
39
+ out = sys.stdout
40
+ self._out = out
41
+
42
+ self._i = 0
43
+ self._elapsed = 0.
44
+ self._last_print = 0.
45
+
46
+ def render(
47
+ self,
48
+ *,
49
+ complete: bool = False,
50
+ ) -> str:
51
+ iter_per_sec = self._i / self._elapsed if self._elapsed > 0 else 0
52
+
53
+ if self._total is not None:
54
+ remaining = (self._total - self._i) / iter_per_sec if iter_per_sec > 0 else 0
55
+ done = int(self._length * self._i / self._total)
56
+
57
+ bar = f'[{"█" * done}{"." * (self._length - done)}]'
58
+ info_parts = [
59
+ f'{self._i}/{self._total}',
60
+ f'{iter_per_sec:.2f} it/s',
61
+ f'{self._elapsed:.2f}s elapsed',
62
+ f'{remaining:.2f}s left',
63
+ ]
64
+
65
+ else:
66
+ bar = f'[{("█" if complete else "?") * self._length}]'
67
+ info_parts = [
68
+ f'{self._i}',
69
+ f'{iter_per_sec:.2f} it/s',
70
+ f'{self._elapsed:.2f}s elapsed',
71
+ ]
72
+
73
+ info = ' | '.join(info_parts)
74
+ return f'{bar} {info}'
75
+
76
+ def print(
77
+ self,
78
+ *,
79
+ now: float | None = None,
80
+ **kwargs: ta.Any,
81
+ ) -> None:
82
+ if now is None:
83
+ now = time.time()
84
+
85
+ line = self.render(**kwargs)
86
+ self._out.write(f'\033[2K\033[G{line}')
87
+ self._out.flush()
88
+
89
+ self._last_print = now
90
+
91
+ def update(
92
+ self,
93
+ n: int = 1,
94
+ *,
95
+ now: float | None = None,
96
+ silent: bool = False,
97
+ ) -> None:
98
+ if now is None:
99
+ now = time.time()
100
+
101
+ self._i += n
102
+ self._elapsed = now - self._start_time
103
+
104
+ if not silent:
105
+ if now - self._last_print >= self._interval:
106
+ self.print(now=now)
107
+
108
+
109
+ def progress_bar(
110
+ seq: ta.Iterable[T],
111
+ *,
112
+ no_tty_check: bool = False,
113
+ total: int | None = None,
114
+ out: ta.TextIO | None = None,
115
+ **kwargs: ta.Any,
116
+ ) -> ta.Generator[T, None, None]:
117
+ if out is None:
118
+ out = sys.stdout
119
+
120
+ if not no_tty_check and not out.isatty():
121
+ yield from seq
122
+ return
123
+
124
+ if total is None:
125
+ if isinstance(seq, ta.Sized):
126
+ total = len(seq)
127
+
128
+ pb = ProgressBar(
129
+ total=total,
130
+ out=out,
131
+ **kwargs,
132
+ )
133
+
134
+ for item in seq:
135
+ pb.update()
136
+ yield item
137
+
138
+ pb.print(complete=True)
139
+ out.write('\n')
omlish/term/vt100.py ADDED
@@ -0,0 +1,271 @@
1
+ import dataclasses as dc
2
+ import string
3
+ import typing as ta
4
+
5
+ from omlish.lite.check import check
6
+
7
+
8
+ T = ta.TypeVar('T')
9
+
10
+
11
+ ##
12
+
13
+
14
+ @dc.dataclass()
15
+ class Cell:
16
+ """Represents a single character cell in the terminal with attributes."""
17
+
18
+ char: str = ' '
19
+
20
+ fg: str = 'default'
21
+ bg: str = 'default'
22
+
23
+ bold: bool = False
24
+ underline: bool = False
25
+ reverse: bool = False
26
+
27
+ def __repr__(self):
28
+ return f'Cell({self.char!r}, bold={self.bold}, underline={self.underline})'
29
+
30
+
31
+ class Vt100Terminal:
32
+ def __init__(
33
+ self,
34
+ rows: int = 24,
35
+ cols: int = 80,
36
+ ) -> None:
37
+ super().__init__()
38
+
39
+ self._rows = rows
40
+ self._cols = cols
41
+
42
+ # 2D array of Cell objects
43
+ self._screen = [
44
+ [Cell() for _ in range(cols)]
45
+ for _ in range(rows)
46
+ ]
47
+
48
+ # Current cursor position (row, col), 0-based internally
49
+ self._cursor_row = 0
50
+ self._cursor_col = 0
51
+
52
+ # Current text attributes
53
+ self._current_fg = 'default'
54
+ self._current_bg = 'default'
55
+ self._current_bold = False
56
+ self._current_underline = False
57
+ self._current_reverse = False
58
+
59
+ # Parser state
60
+ self._state: ta.Literal['normal', 'esc', 'csi'] = 'normal'
61
+ self._escape_buffer: list[str] = []
62
+
63
+ def parse_byte(self, byte: int | str) -> None:
64
+ """Parse a single byte of input (as an integer or a single-character string)."""
65
+
66
+ if isinstance(byte, int):
67
+ byte = chr(byte)
68
+
69
+ if self._state == 'normal':
70
+ if byte == '\x1b':
71
+ # Start of escape sequence
72
+ self._state = 'esc'
73
+ self._escape_buffer = [byte]
74
+ elif byte == '\r':
75
+ # Carriage return
76
+ self._cursor_col = 0
77
+ elif byte == '\n':
78
+ # Line feed
79
+ self._cursor_row = min(self._cursor_row + 1, self._rows - 1)
80
+ elif byte == '\b':
81
+ # Backspace
82
+ self._cursor_col = max(self._cursor_col - 1, 0)
83
+ elif byte in string.printable and byte not in ['\x0b', '\x0c']:
84
+ # Printable ASCII (excluding form feeds, vertical tabs, etc.)
85
+ self._put_char(byte)
86
+ else:
87
+ # Ignore other control characters
88
+ pass
89
+
90
+ elif self._state == 'esc':
91
+ self._escape_buffer.append(byte)
92
+ if byte == '[':
93
+ # Move to CSI state (Control Sequence Introducer)
94
+ self._state = 'csi'
95
+ # Some escape codes like ESCc, ESC7, etc. are possible but we'll ignore or handle them in a minimal way. If
96
+ # no further instructions, revert to normal
97
+ elif len(self._escape_buffer) == 2:
98
+ # We only support ESC + [ in this example, so revert
99
+ self._state = 'normal'
100
+
101
+ elif self._state == 'csi':
102
+ self._escape_buffer.append(byte)
103
+ # Check if this byte ends the sequence (typical final bytes are A-Z, @, etc.)
104
+ if byte.isalpha() or byte in '@`~':
105
+ # We have a complete CSI sequence: parse it
106
+ self._parse_csi(''.join(self._escape_buffer))
107
+ # Reset state
108
+ self._state = 'normal'
109
+ self._escape_buffer = []
110
+
111
+ def _put_char(self, ch: str) -> None:
112
+ """Write a character at the current cursor position, and advance the cursor."""
113
+
114
+ if 0 <= self._cursor_row < self._rows and 0 <= self._cursor_col < self._cols:
115
+ cell = self._screen[self._cursor_row][self._cursor_col]
116
+ cell.char = ch
117
+ cell.fg = self._current_fg
118
+ cell.bg = self._current_bg
119
+ cell.bold = self._current_bold
120
+ cell.underline = self._current_underline
121
+ cell.reverse = self._current_reverse
122
+
123
+ self._cursor_col += 1
124
+ if self._cursor_col >= self._cols:
125
+ self._cursor_col = 0
126
+ self._cursor_row = min(self._cursor_row + 1, self._rows - 1)
127
+
128
+ def _parse_csi(self, seq: str) -> None:
129
+ """
130
+ Parse a CSI (Control Sequence Introducer) escape sequence. Typically looks like: ESC [ parameters letter For
131
+ example: ESC [ 2 J, ESC [ 10 ; 20 H, etc.
132
+ """
133
+
134
+ # seq includes the initial ESC[. e.g. "\x1b[10;20H"
135
+ # We'll strip ESC[ and parse what remains.
136
+ check.state(seq.startswith('\x1b['))
137
+ body = seq[2:] # everything after ESC[
138
+
139
+ # Find final character that determines the command (e.g. 'A', 'B', 'H', 'J', 'K', 'm', etc.)
140
+ final = body[-1]
141
+ params = body[:-1]
142
+
143
+ # Split params by semicolons, handle empty as '0'
144
+ if params.strip() == '':
145
+ numbers = [0]
146
+ else:
147
+ numbers = [int(x) if x.isdigit() else 0 for x in params.split(';')]
148
+
149
+ if final in 'ABCD':
150
+ # Cursor movement
151
+ n = numbers[0] if numbers else 1
152
+ if final == 'A':
153
+ # Up
154
+ self._cursor_row = max(self._cursor_row - n, 0)
155
+ elif final == 'B':
156
+ # Down
157
+ self._cursor_row = min(self._cursor_row + n, self._rows - 1)
158
+ elif final == 'C':
159
+ # Right
160
+ self._cursor_col = min(self._cursor_col + n, self._cols - 1)
161
+ elif final == 'D':
162
+ # Left
163
+ self._cursor_col = max(self._cursor_col - n, 0)
164
+
165
+ elif final in 'Hf':
166
+ # Cursor position
167
+ # CSI row;colH or CSI row;colf (1-based coords)
168
+ row = numbers[0] if len(numbers) > 0 else 1
169
+ col = numbers[1] if len(numbers) > 1 else 1
170
+ self._cursor_row = min(max(row - 1, 0), self._rows - 1)
171
+ self._cursor_col = min(max(col - 1, 0), self._cols - 1)
172
+
173
+ elif final == 'J':
174
+ # Erase display
175
+ # n=0 -> clear from cursor down,
176
+ # n=1 -> clear from cursor up,
177
+ # n=2 -> clear entire screen
178
+ n = numbers[0] if numbers else 0
179
+ if n == 2:
180
+ self._clear_screen()
181
+ elif n == 0:
182
+ self._clear_down()
183
+ elif n == 1:
184
+ self._clear_up()
185
+ # else: unsupported J mode, ignore
186
+
187
+ elif final == 'K':
188
+ # Erase line
189
+ # n=0 -> clear from cursor right
190
+ # n=1 -> clear from cursor left
191
+ # n=2 -> clear entire line
192
+ n = numbers[0] if numbers else 0
193
+ if n == 2:
194
+ self._clear_line(self._cursor_row)
195
+ elif n == 0:
196
+ self._clear_right(self._cursor_row, self._cursor_col)
197
+ elif n == 1:
198
+ self._clear_left(self._cursor_row, self._cursor_col)
199
+ # else: ignore
200
+
201
+ elif final == 'm':
202
+ # SGR - Select Graphic Rendition
203
+ # We handle a subset: 0 (reset), 1 (bold), 4 (underline), 7 (reverse)
204
+ # Colors could be extended, but here we keep it minimal
205
+ for code in numbers:
206
+ if code == 0:
207
+ self._current_fg = 'default'
208
+ self._current_bg = 'default'
209
+ self._current_bold = False
210
+ self._current_underline = False
211
+ self._current_reverse = False
212
+ elif code == 1:
213
+ self._current_bold = True
214
+ elif code == 4:
215
+ self._current_underline = True
216
+ elif code == 7:
217
+ self._current_reverse = True
218
+ # You can add more codes for color, etc.
219
+ else:
220
+ # Unsupported SGR code - ignore gracefully
221
+ pass
222
+
223
+ else:
224
+ # Unsupported final - ignore gracefully
225
+ pass
226
+
227
+ # hods for Erase Operations
228
+
229
+ def _clear_screen(self) -> None:
230
+ for r in range(self._rows):
231
+ for c in range(self._cols):
232
+ self._screen[r][c] = Cell()
233
+
234
+ def _clear_down(self) -> None:
235
+ """Clear from cursor to the end of the screen."""
236
+
237
+ # Clear current line from cursor forward
238
+ self._clear_right(self._cursor_row, self._cursor_col)
239
+
240
+ # Clear all lines below cursor
241
+ for r in range(self._cursor_row + 1, self._rows):
242
+ self._clear_line(r)
243
+
244
+ def _clear_up(self) -> None:
245
+ """Clear from the start of the screen up to the cursor."""
246
+
247
+ # Clear current line from start to cursor
248
+ self._clear_left(self._cursor_row, self._cursor_col)
249
+
250
+ # Clear all lines above cursor
251
+ for r in range(self._cursor_row):
252
+ self._clear_line(r)
253
+
254
+ def _clear_line(self, row: int) -> None:
255
+ for c in range(self._cols):
256
+ self._screen[row][c] = Cell()
257
+
258
+ def _clear_right(self, row: int, col: int) -> None:
259
+ for c in range(col, self._cols):
260
+ self._screen[row][c] = Cell()
261
+
262
+ def _clear_left(self, row: int, col: int) -> None:
263
+ for c in range(col + 1):
264
+ self._screen[row][c] = Cell()
265
+
266
+ # Debug/Utility Methods
267
+
268
+ def get_screen_as_strings(self) -> list[str]:
269
+ """Return a list of strings representing each row (ignoring attributes). Useful for debugging/testing."""
270
+
271
+ return [''.join(cell.char for cell in row) for row in self._screen]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: omlish
3
- Version: 0.0.0.dev186
3
+ Version: 0.0.0.dev188
4
4
  Summary: omlish
5
5
  Author: wrmsr
6
6
  License: BSD-3-Clause
@@ -1,5 +1,5 @@
1
1
  omlish/.manifests.json,sha256=lRkBDFxlAbf6lN5upo3WSf-owW8YG1T21dfpbQL-XHM,7598
2
- omlish/__about__.py,sha256=wXDZM7tzxcfyOLK-VT5C8uTRMAMZBIy92cM1XjLujTQ,3409
2
+ omlish/__about__.py,sha256=qa6ubyF59208I-I_fc4EQ_bi5Jw1Ytht_Nnwgez5qFg,3409
3
3
  omlish/__init__.py,sha256=SsyiITTuK0v74XpKV8dqNaCmjOlan1JZKrHQv5rWKPA,253
4
4
  omlish/c3.py,sha256=ubu7lHwss5V4UznbejAI0qXhXahrU01MysuHOZI9C4U,8116
5
5
  omlish/cached.py,sha256=UI-XTFBwA6YXWJJJeBn-WkwBkfzDjLBBaZf4nIJA9y0,510
@@ -13,7 +13,6 @@ omlish/runmodule.py,sha256=PWvuAaJ9wQQn6bx9ftEL3_d04DyotNn8dR_twm2pgw0,700
13
13
  omlish/shlex.py,sha256=bsW2XUD8GiMTUTDefJejZ5AyqT1pTgWMPD0BMoF02jE,248
14
14
  omlish/subprocesses.py,sha256=n6pk0nUaTFHzD_A6duyKNJ4ggncU7uNepfh_T90etHE,8671
15
15
  omlish/sync.py,sha256=QJ79kxmIqDP9SeHDoZAf--DpFIhDQe1jACy8H4N0yZI,2928
16
- omlish/term.py,sha256=EVHm3lEEIc9hT4f8BPmzbNUwlqZ8nrRpCwyQMN7LBm0,9313
17
16
  omlish/antlr/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
18
17
  omlish/antlr/delimit.py,sha256=3Byvh9_Ip8ftM_SeSEmMbnNo1jrxk-xm8HnHDp_nDaI,3466
19
18
  omlish/antlr/dot.py,sha256=uH2X7-8xNLYDQNJ30uW8ssv1MLkZSm07GsalcRuunYI,817
@@ -395,7 +394,7 @@ omlish/lite/typing.py,sha256=U3-JaEnkDSYxK4tsu_MzUn3RP6qALBe5FXQXpD-licE,1090
395
394
  omlish/logs/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
396
395
  omlish/logs/abc.py,sha256=ho4ABKYMKX-V7g4sp1BByuOLzslYzLlQ0MESmjEpT-o,8005
397
396
  omlish/logs/all.py,sha256=4Z6cNB0E1xbX0IOQWZEWLA0Jw-yyjhXa3PD_Nvfewu8,556
398
- omlish/logs/color.py,sha256=02feYPZm4A7qHeBABpiar2J2E6tf-vtw1pOQAsJs_1c,668
397
+ omlish/logs/color.py,sha256=CM-ceoPXs0j5_klnZDJkFCFRgHOToFzJyLjC6LsnPEk,665
399
398
  omlish/logs/configs.py,sha256=XOc8rWxfPpPMxJESVD2mLCUoLtbQnGnZwvYhhqe7DD8,772
400
399
  omlish/logs/filters.py,sha256=2noFRyBez3y519fpfsDSt1vo8wX-85b8sMXZi5o_xyE,208
401
400
  omlish/logs/handlers.py,sha256=zgSnKQA5q9Fu7T0Nkd7twog9H1Wg9-bDCzz4_F1TOBo,319
@@ -545,6 +544,10 @@ omlish/sql/tabledefs/elements.py,sha256=lP_Ch19hKmiGYPQVeC8HpFaKdTYnXi2FfpfwKMxZ
545
544
  omlish/sql/tabledefs/lower.py,sha256=YQf8gl1kxD5Fm-vOxV6G0Feh_D9PP1pYwz_vz6XjTPQ,1405
546
545
  omlish/sql/tabledefs/marshal.py,sha256=j-Rz1HsiXmABv39-2VoJdzSSB3kbxqaVevbdkZWMyG8,504
547
546
  omlish/sql/tabledefs/tabledefs.py,sha256=lIhvlt0pk6G7RZAtDFsFXm5j0l9BvRfnP7vNGeydHtE,816
547
+ omlish/term/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
548
+ omlish/term/codes.py,sha256=gqouA7KDyGzyCmmu8ejGhT_8XJGsejdTQh2pSCMbWAQ,6150
549
+ omlish/term/progressbar.py,sha256=TiwdmPSMa5jQj35i1NQURTWQGy4eWUNx_XiPM38JtvQ,3184
550
+ omlish/term/vt100.py,sha256=sYLddUSrubCJPnI6f2t5Pzp0tFGLLBKyYdDfKmkwREM,9363
548
551
  omlish/testing/__init__.py,sha256=M_BQrcCHkoL-ZvE-UpQ8XxXNYRRawhjUz4rCJnAqM2A,152
549
552
  omlish/testing/testing.py,sha256=TT2wwSzPZ_KhIvKxpM1qc1yHKD-LHDNgGrcr_h8vs7c,2895
550
553
  omlish/testing/pytest/__init__.py,sha256=B2nyJrjIoNcEopbg0IZ5UUDs4OHmQ8qqElFJfGcDdas,257
@@ -573,9 +576,9 @@ omlish/text/indent.py,sha256=6Jj6TFY9unaPa4xPzrnZemJ-fHsV53IamP93XGjSUHs,1274
573
576
  omlish/text/minja.py,sha256=KAmZ2POcLcxwF4DPKxdWa16uWxXmVz1UnJXLSwt4oZo,5761
574
577
  omlish/text/parts.py,sha256=7vPF1aTZdvLVYJ4EwBZVzRSy8XB3YqPd7JwEnNGGAOo,6495
575
578
  omlish/text/random.py,sha256=jNWpqiaKjKyTdMXC-pWAsSC10AAP-cmRRPVhm59ZWLk,194
576
- omlish-0.0.0.dev186.dist-info/LICENSE,sha256=B_hVtavaA8zCYDW99DYdcpDLKz1n3BBRjZrcbv8uG8c,1451
577
- omlish-0.0.0.dev186.dist-info/METADATA,sha256=LcDvtkMJac-ZP6AFNic3Fw7fnCM6SqxCEM1QlUZDP-c,4264
578
- omlish-0.0.0.dev186.dist-info/WHEEL,sha256=PZUExdf71Ui_so67QXpySuHtCi3-J3wvF4ORK6k_S8U,91
579
- omlish-0.0.0.dev186.dist-info/entry_points.txt,sha256=Lt84WvRZJskWCAS7xnQGZIeVWksprtUHj0llrvVmod8,35
580
- omlish-0.0.0.dev186.dist-info/top_level.txt,sha256=pePsKdLu7DvtUiecdYXJ78iO80uDNmBlqe-8hOzOmfs,7
581
- omlish-0.0.0.dev186.dist-info/RECORD,,
579
+ omlish-0.0.0.dev188.dist-info/LICENSE,sha256=B_hVtavaA8zCYDW99DYdcpDLKz1n3BBRjZrcbv8uG8c,1451
580
+ omlish-0.0.0.dev188.dist-info/METADATA,sha256=B8AyLpz8d0yITN04acY73gPMv_SDjqP5Z7lGCaOwpS0,4264
581
+ omlish-0.0.0.dev188.dist-info/WHEEL,sha256=PZUExdf71Ui_so67QXpySuHtCi3-J3wvF4ORK6k_S8U,91
582
+ omlish-0.0.0.dev188.dist-info/entry_points.txt,sha256=Lt84WvRZJskWCAS7xnQGZIeVWksprtUHj0llrvVmod8,35
583
+ omlish-0.0.0.dev188.dist-info/top_level.txt,sha256=pePsKdLu7DvtUiecdYXJ78iO80uDNmBlqe-8hOzOmfs,7
584
+ omlish-0.0.0.dev188.dist-info/RECORD,,