68 lines
2.2 KiB
Python
68 lines
2.2 KiB
Python
import os
|
|
import math
|
|
import collections
|
|
from typing import BinaryIO, Generator, Callable, Union as U, Any, cast
|
|
from contextlib import contextmanager
|
|
|
|
|
|
def bits(v: int, s: int, l: int) -> int:
|
|
return (v >> s) & ((1 << l) - 1)
|
|
|
|
def bit(v: int, s: int) -> int:
|
|
return bits(v, s, 1)
|
|
|
|
Pos = U[int, float]
|
|
|
|
|
|
@contextmanager
|
|
def seeking(fd: BinaryIO, pos: Pos, whence: int = os.SEEK_SET) -> Generator[BinaryIO, None, None]:
|
|
oldpos = fd.tell()
|
|
fd.seek(cast(int, pos), whence)
|
|
try:
|
|
yield fd
|
|
finally:
|
|
fd.seek(oldpos, os.SEEK_SET)
|
|
|
|
|
|
def indent(s: str, count: int, start: bool = False) -> str:
|
|
""" Indent all lines of a string. """
|
|
lines = s.splitlines()
|
|
for i in range(0 if start else 1, len(lines)):
|
|
lines[i] = ' ' * count + lines[i]
|
|
return '\n'.join(lines)
|
|
|
|
def format_bytes(bs: bytes) -> str:
|
|
return '[' + ' '.join(hex(b)[2:].zfill(2) for b in bs) + ']'
|
|
|
|
def format_value(value: Any, formatter: Callable[[Any], str], indentation: int = 0) -> str:
|
|
""" Format containers to use the given formatter function instead of always repr(). """
|
|
if isinstance(value, (dict, collections.Mapping)):
|
|
if value:
|
|
fmt = '{{\n{}\n}}'
|
|
values = [indent(',\n'.join('{}: {}'.format(
|
|
format_value(k, formatter),
|
|
format_value(v, formatter)
|
|
) for k, v in value.items()), 2, True)]
|
|
else:
|
|
fmt = '{{}}'
|
|
values = []
|
|
elif isinstance(value, (list, set, frozenset)):
|
|
l = len(value)
|
|
is_set = isinstance(value, (set, frozenset))
|
|
if l > 3:
|
|
fmt = '{{\n{}\n}}' if is_set else '[\n{}\n]'
|
|
values = [indent(',\n'.join(format_value(v, formatter) for v in value), 2, True)]
|
|
elif l > 0:
|
|
fmt = '{{{}}}' if is_set else '[{}]'
|
|
values = [', '.join(format_value(v, formatter) for v in value)]
|
|
else:
|
|
fmt = '{{}}' if is_set else '[]'
|
|
values = []
|
|
elif isinstance(value, (bytes, bytearray)):
|
|
fmt = '{}'
|
|
values = [format_bytes(value)]
|
|
else:
|
|
fmt = '{}'
|
|
values = [formatter(value)]
|
|
return indent(fmt.format(*values), indentation)
|