add Bit object
This commit is contained in:
parent
b887264cbc
commit
37782f4b8b
|
@ -1 +1 @@
|
||||||
from .main import Bits, Bytes
|
from .main import Bit, Bits, Bytes
|
||||||
|
|
133
bits/main.py
133
bits/main.py
|
@ -1,8 +1,80 @@
|
||||||
|
|
||||||
class Bits(int):
|
class Bit:
|
||||||
|
"""
|
||||||
|
Provide a flexible bit representation
|
||||||
|
"""
|
||||||
|
|
||||||
def __init__(self, byte):
|
def __init__(self, var):
|
||||||
self.__setvalue(byte)
|
if not isinstance(var, (bool, int, str, Bit)):
|
||||||
|
raise TypeError("Bit must be one of type (int, bool, str)")
|
||||||
|
if isinstance(var, (int, Bit)):
|
||||||
|
if var < 0 or var > 1:
|
||||||
|
raise ValueError("Bit must be 0 or 1")
|
||||||
|
self.__bit = bool(var)
|
||||||
|
elif isinstance(var, str):
|
||||||
|
if var == "0" or var == "1":
|
||||||
|
self.__bit = bool(int(var))
|
||||||
|
else:
|
||||||
|
raise ValueError('Bit must be "0" or "1"')
|
||||||
|
elif isinstance(var, bool):
|
||||||
|
self.__bit = var
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return str(int(self.__bit))
|
||||||
|
|
||||||
|
def __int__(self):
|
||||||
|
return int(self.__bit)
|
||||||
|
|
||||||
|
def __bool__(self):
|
||||||
|
return self.__bit
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return f'{self.__class__.__name__}({self.__bit!r})'
|
||||||
|
|
||||||
|
def __eq__(self, compare):
|
||||||
|
# Massage compare to bool
|
||||||
|
return bool(self) == bool(Bit(compare))
|
||||||
|
|
||||||
|
def __ne__(self, compare):
|
||||||
|
return bool(self) != bool(Bit(compare))
|
||||||
|
|
||||||
|
def __lt__(self, compare):
|
||||||
|
return bool(self) < bool(Bit(compare))
|
||||||
|
|
||||||
|
def __le__(self, compare):
|
||||||
|
return bool(self) <= bool(Bit(compare))
|
||||||
|
|
||||||
|
def __gt__(self, compare):
|
||||||
|
return bool(self) > bool(Bit(compare))
|
||||||
|
|
||||||
|
def __ge__(self, compare):
|
||||||
|
return bool(self) >= bool(Bit(compare))
|
||||||
|
|
||||||
|
def __get__(self, instance, owner):
|
||||||
|
return self.__bit
|
||||||
|
|
||||||
|
def __hash__(self):
|
||||||
|
return hash(self.__bit)
|
||||||
|
|
||||||
|
def toggle(self):
|
||||||
|
self.__bit = not self.__bit
|
||||||
|
|
||||||
|
@property
|
||||||
|
def lie(self):
|
||||||
|
"""
|
||||||
|
Return the opposite of bit, without changing the bit state
|
||||||
|
"""
|
||||||
|
return not self.__bit
|
||||||
|
|
||||||
|
|
||||||
|
class Bits:
|
||||||
|
"""
|
||||||
|
Provide bit operation helpers.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, var=0):
|
||||||
|
self.__value = 0
|
||||||
|
self.__setvalue(var)
|
||||||
|
|
||||||
def __bytes__(self):
|
def __bytes__(self):
|
||||||
return bytes([self.__value])
|
return bytes([self.__value])
|
||||||
|
@ -21,6 +93,9 @@ class Bits(int):
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.bin()
|
return self.bin()
|
||||||
|
|
||||||
|
def __ord__(self):
|
||||||
|
return int(self)
|
||||||
|
|
||||||
def __index__(self):
|
def __index__(self):
|
||||||
return int(self)
|
return int(self)
|
||||||
|
|
||||||
|
@ -46,49 +121,46 @@ class Bits(int):
|
||||||
return int(self) >= int(Bits(compare))
|
return int(self) >= int(Bits(compare))
|
||||||
|
|
||||||
def __getitem__(self, index):
|
def __getitem__(self, index):
|
||||||
return bool(int(self.bin()[index])) #self.bool(key)
|
return self.list()[index]
|
||||||
|
|
||||||
def __setitem__(self, index, value):
|
def __setitem__(self, index, value):
|
||||||
# we'll take the easy way for now
|
# we'll take the easy way for now
|
||||||
s = list(self.bin())
|
l = self.list()
|
||||||
# str->int->bool->int : accept bool, str, int, return either "0" or "1"
|
# str->int->bool->int : accept bool, str, int, return either "0" or "1"
|
||||||
s[index] = str(int(bool(int(value))))
|
l[index] = bool(int(value))
|
||||||
self.__setvalue("".join(s))
|
self.__setvalue(l)
|
||||||
|
|
||||||
def __get__(self, instance, owner):
|
def __get__(self, instance, owner):
|
||||||
return self.bin()
|
return self.bin()
|
||||||
|
|
||||||
def __hash__(self):
|
def __setvalue(self, var):
|
||||||
return None
|
|
||||||
|
|
||||||
def __setvalue(self, value):
|
|
||||||
self.__value = 0
|
self.__value = 0
|
||||||
if isinstance(value, int):
|
if isinstance(var, int):
|
||||||
if value < 0 or value > 255:
|
if var < 0 or var > 255:
|
||||||
raise ValueError("Integer must be between 0 and 255")
|
raise ValueError("Integer must be between 0 and 255")
|
||||||
self.__value = value
|
self.__value = var
|
||||||
elif isinstance(value, bytes):
|
elif isinstance(var, bytes):
|
||||||
val = 0
|
for _byte in var:
|
||||||
for _byte in value:
|
self.__value += _byte
|
||||||
val += _byte
|
if ret > 255 or ret < 0:
|
||||||
if val > 255 or val < 0:
|
|
||||||
raise ValueError("Sum value of bytes must be between 0"
|
raise ValueError("Sum value of bytes must be between 0"
|
||||||
" and 255")
|
" and 255")
|
||||||
self.__value = val
|
elif (len(var) > 0) and (len(var) <=8):
|
||||||
elif (len(value) > 0) and (len(value) <=8):
|
for bit in range(0, len(var)):
|
||||||
# TODO: apparently, can't use a list when inheriting from 'int'
|
self.__value += (int(bool(int(var[bit]))) *
|
||||||
for bit in range(0, len(value)):
|
(2 ** (len(var) - 1 - bit)))
|
||||||
self.__value += (int(bool(int(value[bit]))) *
|
|
||||||
(2 ** (len(value) - 1 - bit)))
|
|
||||||
else:
|
else:
|
||||||
raise TypeError("Expected object with len <= 8")
|
raise TypeError("Expected object with len <= 8")
|
||||||
|
|
||||||
def bin(self, pad=True):
|
def bin(self, pad=True, reverse=False):
|
||||||
bitcount = self.__value.bit_length()
|
bitcount = self.__value.bit_length()
|
||||||
ret = ""
|
ret = ""
|
||||||
if pad and ((bitcount % 8) > 0):
|
if pad and ((bitcount % 8) > 0):
|
||||||
ret = "0" * (8 - (bitcount % 8))
|
ret = "0" * (8 - (bitcount % 8))
|
||||||
return ret + bin(self.__value)[2:]
|
ret += bin(self.__value)[2:]
|
||||||
|
if reverse:
|
||||||
|
ret = ret[::-1]
|
||||||
|
return ret
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def chr(self):
|
def chr(self):
|
||||||
|
@ -113,6 +185,13 @@ class Bits(int):
|
||||||
def nibble(self):
|
def nibble(self):
|
||||||
return [self.bin()[:4], self.bin()[4:]]
|
return [self.bin()[:4], self.bin()[4:]]
|
||||||
|
|
||||||
|
def list(self, pad=True, reverse=False):
|
||||||
|
ret = []
|
||||||
|
bits = self.bin(pad=pad, reverse=reverse)
|
||||||
|
for bit in bits:
|
||||||
|
ret.append(bool(int(bit)))
|
||||||
|
return ret
|
||||||
|
|
||||||
|
|
||||||
class Bytes(bytearray):
|
class Bytes(bytearray):
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,7 @@ import sys
|
||||||
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__),
|
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__),
|
||||||
'..')))
|
'..')))
|
||||||
|
|
||||||
|
from bits import Bit
|
||||||
from bits import Bits
|
from bits import Bits
|
||||||
from bits import Bytes
|
from bits import Bytes
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,82 @@
|
||||||
|
from unittest import TestCase
|
||||||
|
|
||||||
|
from .context import Bit
|
||||||
|
|
||||||
|
class TestBit(TestCase):
|
||||||
|
def setUp(self):
|
||||||
|
self.true = ["1", 1, True]
|
||||||
|
self.false = ["0", 0, False]
|
||||||
|
pass
|
||||||
|
|
||||||
|
def test_init(self):
|
||||||
|
"""
|
||||||
|
Test creation of bit object
|
||||||
|
"""
|
||||||
|
for t in self.true:
|
||||||
|
self.assertTrue(Bit(t), f"Bit({t}) == True")
|
||||||
|
for f in self.false:
|
||||||
|
self.assertFalse(Bit(f), f"Bit({f}) == False")
|
||||||
|
|
||||||
|
def test_conversion(self):
|
||||||
|
"""
|
||||||
|
Test the conversion methods
|
||||||
|
"""
|
||||||
|
self.assertEqual(str(Bit(True)), "1", f'str(Bit(True)) == "1"')
|
||||||
|
self.assertEqual(str(Bit(False)), "0", f'str(Bit(False)) == "0"')
|
||||||
|
self.assertEqual(int(Bit(True)), 1, f'int(Bit(True)) == 1')
|
||||||
|
self.assertEqual(int(Bit(False)), 0, f'int(Bit(False)) == 0')
|
||||||
|
self.assertEqual(bool(Bit(True)), True, f'bool(Bit(True)) == True')
|
||||||
|
self.assertEqual(bool(Bit(False)), False, f'bool(Bit(False)) == False')
|
||||||
|
|
||||||
|
def test_comparison(self):
|
||||||
|
"""
|
||||||
|
Test the comparison operators
|
||||||
|
"""
|
||||||
|
self.assertNotEqual(Bit(False), Bit(True), 'Bit(False) != Bit(True)')
|
||||||
|
self.assertNotEqual(Bit(True), Bit(False), 'Bit(True) != Bit(False)')
|
||||||
|
self.assertLess(Bit(False), Bit(True), 'Bit(False) < Bit(True)')
|
||||||
|
self.assertGreater(Bit(True), Bit(False), 'Bit(True) > Bit(False)')
|
||||||
|
self.assertLessEqual(Bit(False), Bit(True), 'Bit(False) <= Bit(True)')
|
||||||
|
self.assertLessEqual(Bit(True), Bit(True), 'Bit(True) <= Bit(True)')
|
||||||
|
self.assertLessEqual(Bit(False), Bit(False),
|
||||||
|
'Bit(False) <= Bit(False)')
|
||||||
|
self.assertGreaterEqual(Bit(True), Bit(False),
|
||||||
|
'Bit(True) >= Bit(False)')
|
||||||
|
self.assertGreaterEqual(Bit(True), Bit(True),
|
||||||
|
'Bit(True) >= Bit(True)')
|
||||||
|
self.assertGreaterEqual(Bit(False), Bit(False),
|
||||||
|
'Bit(False) >= Bit(False)')
|
||||||
|
|
||||||
|
def test_lie(self):
|
||||||
|
"""
|
||||||
|
Test the lie function/property
|
||||||
|
"""
|
||||||
|
self.assertFalse(Bit(True).lie, "Bit(True).lie == False")
|
||||||
|
self.assertTrue(Bit(False).lie, "Bit(False).lie == True")
|
||||||
|
|
||||||
|
def test_toggle(self):
|
||||||
|
"""
|
||||||
|
Test the toggle function
|
||||||
|
"""
|
||||||
|
var = Bit(True)
|
||||||
|
var.toggle()
|
||||||
|
self.assertFalse(var, "Bit(True).toggle() == False")
|
||||||
|
var.toggle()
|
||||||
|
self.assertTrue(var, "Bit(False).toggle() == True")
|
||||||
|
|
||||||
|
def test_errors(self):
|
||||||
|
"""
|
||||||
|
Test that errors are raised for invalid values
|
||||||
|
"""
|
||||||
|
with self.subTest("Raise TypeError"):
|
||||||
|
self.assertRaises(TypeError, Bit, 12.7)
|
||||||
|
self.assertRaises(TypeError, Bit, b'xyz')
|
||||||
|
self.assertRaises(TypeError, Bit, [True, False])
|
||||||
|
self.assertRaises(TypeError, Bit, [True])
|
||||||
|
|
||||||
|
with self.subTest("Raise ValueError"):
|
||||||
|
self.assertRaises(ValueError, Bit, "2")
|
||||||
|
self.assertRaises(ValueError, Bit, 2)
|
||||||
|
self.assertRaises(ValueError, Bit, -1)
|
||||||
|
self.assertRaises(ValueError, Bit, "-2")
|
||||||
|
|
Loading…
Reference in New Issue