build: Initialize Python virtual environment and install project dependencies.
Some checks failed
WLED CI / wled_build (push) Has been cancelled

This commit is contained in:
2026-02-19 22:31:26 +08:00
parent ca1319462e
commit bb17574d0c
1668 changed files with 469578 additions and 0 deletions

View File

@@ -0,0 +1,7 @@
from .binary import (
int_to_bin, bin_to_int, swap_bytes, encode_bin, decode_bin)
from .bitstream import BitStreamReader, BitStreamWriter
from .container import (Container, FlagsContainer, ListContainer,
LazyContainer)
from .hex import HexString, hexdump

View File

@@ -0,0 +1,117 @@
from .py3compat import int2byte
def int_to_bin(number, width=32):
r"""
Convert an integer into its binary representation in a bytes object.
Width is the amount of bits to generate. If width is larger than the actual
amount of bits required to represent number in binary, sign-extension is
used. If it's smaller, the representation is trimmed to width bits.
Each "bit" is either '\x00' or '\x01'. The MSBit is first.
Examples:
>>> int_to_bin(19, 5)
b'\x01\x00\x00\x01\x01'
>>> int_to_bin(19, 8)
b'\x00\x00\x00\x01\x00\x00\x01\x01'
"""
if number < 0:
number += 1 << width
i = width - 1
bits = bytearray(width)
while number and i >= 0:
bits[i] = number & 1
number >>= 1
i -= 1
return bytes(bits)
_bit_values = {
0: 0,
1: 1,
48: 0, # '0'
49: 1, # '1'
# The following are for Python 2, in which iteration over a bytes object
# yields single-character bytes and not integers.
'\x00': 0,
'\x01': 1,
'0': 0,
'1': 1,
}
def bin_to_int(bits, signed=False):
r"""
Logical opposite of int_to_bin. Both '0' and '\x00' are considered zero,
and both '1' and '\x01' are considered one. Set sign to True to interpret
the number as a 2-s complement signed integer.
"""
number = 0
bias = 0
ptr = 0
if signed and _bit_values[bits[0]] == 1:
bits = bits[1:]
bias = 1 << len(bits)
for b in bits:
number <<= 1
number |= _bit_values[b]
return number - bias
def swap_bytes(bits, bytesize=8):
r"""
Bits is a b'' object containing a binary representation. Assuming each
bytesize bits constitute a bytes, perform a endianness byte swap. Example:
>>> swap_bytes(b'00011011', 2)
b'11100100'
"""
i = 0
l = len(bits)
output = [b""] * ((l // bytesize) + 1)
j = len(output) - 1
while i < l:
output[j] = bits[i : i + bytesize]
i += bytesize
j -= 1
return b"".join(output)
_char_to_bin = {}
_bin_to_char = {}
for i in range(256):
ch = int2byte(i)
bin = int_to_bin(i, 8)
# Populate with for both keys i and ch, to support Python 2 & 3
_char_to_bin[ch] = bin
_char_to_bin[i] = bin
_bin_to_char[bin] = ch
def encode_bin(data):
"""
Create a binary representation of the given b'' object. Assume 8-bit
ASCII. Example:
>>> encode_bin('ab')
b"\x00\x01\x01\x00\x00\x00\x00\x01\x00\x01\x01\x00\x00\x00\x01\x00"
"""
return b"".join(_char_to_bin[ch] for ch in data)
def decode_bin(data):
"""
Locical opposite of decode_bin.
"""
if len(data) & 7:
raise ValueError("Data length must be a multiple of 8")
i = 0
j = 0
l = len(data) // 8
chars = [b""] * l
while j < l:
chars[j] = _bin_to_char[data[i:i+8]]
i += 8
j += 1
return b"".join(chars)

View File

@@ -0,0 +1,77 @@
from .binary import encode_bin, decode_bin
class BitStreamReader(object):
__slots__ = ["substream", "buffer", "total_size"]
def __init__(self, substream):
self.substream = substream
self.total_size = 0
self.buffer = ""
def close(self):
if self.total_size % 8 != 0:
raise ValueError("total size of read data must be a multiple of 8",
self.total_size)
def tell(self):
return self.substream.tell()
def seek(self, pos, whence = 0):
self.buffer = ""
self.total_size = 0
self.substream.seek(pos, whence)
def read(self, count):
if count < 0:
raise ValueError("count cannot be negative")
l = len(self.buffer)
if count == 0:
data = ""
elif count <= l:
data = self.buffer[:count]
self.buffer = self.buffer[count:]
else:
data = self.buffer
count -= l
bytes = count // 8
if count & 7:
bytes += 1
buf = encode_bin(self.substream.read(bytes))
data += buf[:count]
self.buffer = buf[count:]
self.total_size += len(data)
return data
class BitStreamWriter(object):
__slots__ = ["substream", "buffer", "pos"]
def __init__(self, substream):
self.substream = substream
self.buffer = []
self.pos = 0
def close(self):
self.flush()
def flush(self):
bytes = decode_bin("".join(self.buffer))
self.substream.write(bytes)
self.buffer = []
self.pos = 0
def tell(self):
return self.substream.tell() + self.pos // 8
def seek(self, pos, whence = 0):
self.flush()
self.substream.seek(pos, whence)
def write(self, data):
if not data:
return
if type(data) is not str:
raise TypeError("data must be a string, not %r" % (type(data),))
self.buffer.append(data)

View File

@@ -0,0 +1,161 @@
"""
Various containers.
"""
from pprint import pformat
from .py3compat import MutableMapping
def recursion_lock(retval, lock_name = "__recursion_lock__"):
def decorator(func):
def wrapper(self, *args, **kw):
if getattr(self, lock_name, False):
return retval
setattr(self, lock_name, True)
try:
return func(self, *args, **kw)
finally:
setattr(self, lock_name, False)
wrapper.__name__ = func.__name__
return wrapper
return decorator
class Container(MutableMapping):
"""
A generic container of attributes.
Containers are the common way to express parsed data.
"""
def __init__(self, **kw):
self.__dict__ = kw
# The core dictionary interface.
def __getitem__(self, name):
return self.__dict__[name]
def __delitem__(self, name):
del self.__dict__[name]
def __setitem__(self, name, value):
self.__dict__[name] = value
def keys(self):
return self.__dict__.keys()
def __len__(self):
return len(self.__dict__.keys())
# Extended dictionary interface.
def update(self, other):
self.__dict__.update(other)
__update__ = update
def __contains__(self, value):
return value in self.__dict__
# Rich comparisons.
def __eq__(self, other):
try:
return self.__dict__ == other.__dict__
except AttributeError:
return False
def __ne__(self, other):
return not self == other
# Copy interface.
def copy(self):
return self.__class__(**self.__dict__)
__copy__ = copy
# Iterator interface.
def __iter__(self):
return iter(self.__dict__)
def __repr__(self):
return "%s(%s)" % (self.__class__.__name__, repr(self.__dict__))
def __str__(self):
return "%s(%s)" % (self.__class__.__name__, str(self.__dict__))
class FlagsContainer(Container):
"""
A container providing pretty-printing for flags.
Only set flags are displayed.
"""
@recursion_lock("<...>")
def __str__(self):
d = dict((k, self[k]) for k in self
if self[k] and not k.startswith("_"))
return "%s(%s)" % (self.__class__.__name__, pformat(d))
class ListContainer(list):
"""
A container for lists.
"""
__slots__ = ["__recursion_lock__"]
@recursion_lock("[...]")
def __str__(self):
return pformat(self)
class LazyContainer(object):
__slots__ = ["subcon", "stream", "pos", "context", "_value"]
def __init__(self, subcon, stream, pos, context):
self.subcon = subcon
self.stream = stream
self.pos = pos
self.context = context
self._value = NotImplemented
def __eq__(self, other):
try:
return self._value == other._value
except AttributeError:
return False
def __ne__(self, other):
return not (self == other)
def __str__(self):
return self.__pretty_str__()
def __pretty_str__(self, nesting = 1, indentation = " "):
if self._value is NotImplemented:
text = "<unread>"
elif hasattr(self._value, "__pretty_str__"):
text = self._value.__pretty_str__(nesting, indentation)
else:
text = str(self._value)
return "%s: %s" % (self.__class__.__name__, text)
def read(self):
self.stream.seek(self.pos)
return self.subcon._parse(self.stream, self.context)
def dispose(self):
self.subcon = None
self.stream = None
self.context = None
self.pos = None
def _get_value(self):
if self._value is NotImplemented:
self._value = self.read()
return self._value
value = property(_get_value)
has_value = property(lambda self: self._value is not NotImplemented)

View File

@@ -0,0 +1,43 @@
from .py3compat import byte2int, int2byte, bytes2str
# Map an integer in the inclusive range 0-255 to its string byte representation
_printable = dict((i, ".") for i in range(256))
_printable.update((i, bytes2str(int2byte(i))) for i in range(32, 128))
def hexdump(data, linesize):
"""
data is a bytes object. The returned result is a string.
"""
prettylines = []
if len(data) < 65536:
fmt = "%%04X %%-%ds %%s"
else:
fmt = "%%08X %%-%ds %%s"
fmt = fmt % (3 * linesize - 1,)
for i in range(0, len(data), linesize):
line = data[i : i + linesize]
hextext = " ".join('%02x' % byte2int(b) for b in line)
rawtext = "".join(_printable[byte2int(b)] for b in line)
prettylines.append(fmt % (i, str(hextext), str(rawtext)))
return prettylines
class HexString(bytes):
"""
Represents bytes that will be hex-dumped to a string when its string
representation is requested.
"""
def __init__(self, data, linesize = 16):
self.linesize = linesize
def __new__(cls, data, *args, **kwargs):
return bytes.__new__(cls, data)
def __str__(self):
if not self:
return "''"
sep = "\n"
return sep + sep.join(
hexdump(self, self.linesize))

View File

@@ -0,0 +1,74 @@
#-------------------------------------------------------------------------------
# py3compat.py
#
# Some Python2&3 compatibility code
#-------------------------------------------------------------------------------
import sys
PY3 = sys.version_info[0] == 3
try:
from collections.abc import MutableMapping # python >= 3.3
except ImportError:
from collections import MutableMapping # python < 3.3
if PY3:
import io
StringIO = io.StringIO
BytesIO = io.BytesIO
def bchr(i):
""" When iterating over b'...' in Python 2 you get single b'_' chars
and in Python 3 you get integers. Call bchr to always turn this
to single b'_' chars.
"""
return bytes((i,))
def u(s):
return s
def int2byte(i):
return bytes((i,))
def byte2int(b):
return b
def str2bytes(s):
return s.encode("latin-1")
def str2unicode(s):
return s
def bytes2str(b):
return b.decode('latin-1')
def decodebytes(b, encoding):
return bytes(b, encoding)
advance_iterator = next
else:
import cStringIO
StringIO = BytesIO = cStringIO.StringIO
int2byte = chr
byte2int = ord
bchr = lambda i: i
def u(s):
return unicode(s, "unicode_escape")
def str2bytes(s):
return s
def str2unicode(s):
return unicode(s, "unicode_escape")
def bytes2str(b):
return b
def decodebytes(b, encoding):
return b.decode(encoding)
def advance_iterator(it):
return it.next()