add Bit object
This commit is contained in:
		| @@ -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 | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										82
									
								
								tests/test_bit.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										82
									
								
								tests/test_bit.py
									
									
									
									
									
										Normal file
									
								
							| @@ -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") | ||||||
|  |  | ||||||
		Reference in New Issue
	
	Block a user