ssiv/src/ssiv/tools.py

142 lines
4.3 KiB
Python

# This file is part of ssiv.
#
# ssiv is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# ssiv is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with ssiv. If not, see <https://www.gnu.org/licenses/>.
from io import BytesIO
from string import ascii_letters,digits
from struct import pack,unpack,calcsize
from .constants import progname
__all__=[
'str2bytes','bytes2str','packstr','unpackstr',
'prettybytes','prettypath','prettycolor','prettyrect',
'sorter_numeric',
]
_LENFMT='<Q'
_LENLEN=calcsize(_LENFMT)
_printable=tuple(ord(c) for c in f'{ascii_letters}{digits}')
_nums=f'{digits}.'
def str2bytes(s):
return s.encode('utf8') if isinstance(s,str) else s
def bytes2str(b):
return b.decode('utf8') if isinstance(b,bytes) else b
def _packbytes(data):
length=len(data)
return pack(f'{_LENFMT}{length}s',length,data)
def _unpackbytes(buf):
while head:=buf.read(_LENLEN):
try:
yield bytes2str(buf.read(unpack(_LENFMT,head)[0]))
except:
break
def packstr(*args):
return b''.join(_packbytes(str2bytes(s)) for s in args)
def unpackstr(data):
with BytesIO(data) as buf:
return tuple(_unpackbytes(buf))
def prettybytes(data):
return ''.join((chr(n) if n in _printable else f'\\x{n:02x}') for n in data)
def prettypath(container,filename):
return f'{filename}({container})' if container else f'{filename}'
def prettycolor(color):
return '#{}'.format(''.join(f'{c:02X}' for c in color))
def prettyrect(rect):
return '{2}x{3}+{0}+{1}'.format(*rect)
def sorter_numeric(s):
parts=s.split('/')
results=[]
b,_,e=parts[-1].rpartition('.')
if b and e.strip(_nums):
parts[-1:]=[b,e]
else:
parts.append('')
while parts[:-1]:
results.append([])
part=parts.pop(0)
while part.endswith('.'):
part=part[:-1]
parts[0]=f'.{parts[0]}'
while part:
try:
start=min(part.index(s) for s in digits if s in part)
except ValueError:
results[-1].append((part,0))
break
head,part=part[:start],part[start:]
if head:
results[-1].append((head,0))
tail=part.lstrip(_nums)
nstr,part=part[:len(part)-len(tail)],tail
while nstr.endswith('.'):
nstr=nstr[:-1]
part=f'.{part}'
results[-1].append(('',float(nstr)))
results[-1].append(('',0))
results.append([(parts.pop(),0)])
return results
def _logger_init():
from functools import partial
from os import environ
from . import logger
levels=dict(
# level = (bit, error, color)
fault = (1<<0, True, 'red'), # 1
error = (1<<1, True, 'orange'), # 2
warn = (1<<2, True, 'yellow'), # 4
note = (1<<3, False, 'green'), # 8
info = (1<<4, False, 'cyan'), # 16
verb = (1<<5, False, 'lightblue'), # 32
trace = (1<<6, False, 'blue'), # 64
pref = (1<<7, False, 'purple'), # 128
)
level=int(environ.get(f'{progname}_LOG_LEVEL',0))
devel=environ.get(f'{progname}_LOG_DEVEL',None) or None
for api in logger.__all__:
__all__.append(api)
globals()[api]=getattr(logger,api)
for key,(bit,error,color) in levels.items():
__all__.append(key)
globals()[key]=partial(logger.log,devel=devel is not None,
level=key,levelcolor=color,error=error) \
if (bit&level) else logger.true
if level&levels['pref'][0]:
globals()['Pref'].printer=globals()['pref']
globals()['Pref'].disabled=False
return True
assert _logger_init()
# Local Variables:
# coding: utf-8
# mode: python
# python-indent-offset: 4
# indent-tabs-mode: nil
# End: