Skip to content

Commit

Permalink
Some light refactoring.
Browse files Browse the repository at this point in the history
  • Loading branch information
scott-griffiths committed Jun 10, 2024
1 parent b675ddd commit ac09f13
Show file tree
Hide file tree
Showing 6 changed files with 73 additions and 78 deletions.
10 changes: 5 additions & 5 deletions bitformat/array_.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
import numbers
from collections.abc import Sized
from bitformat.exceptions import CreationError
from typing import Union, List, Iterable, Any, Optional, overload, TextIO
from typing import Union, Iterable, Any, overload, TextIO
from bitformat.bits import Bits, BitsType
from bitformat.dtypes import Dtype, dtype_register
from bitformat import utils
Expand Down Expand Up @@ -62,8 +62,8 @@ class Array:
"""

def __init__(self, dtype: Union[str, Dtype], initializer: Optional[Union[int, Array, Iterable, Bits, bytes, bytearray, memoryview]] = None,
trailing_bits: Optional[BitsType] = None) -> None:
def __init__(self, dtype: Union[str, Dtype], initializer: Union[int, Array, Iterable, Bits, bytes, bytearray, memoryview] | None = None,
trailing_bits: BitsType | None = None) -> None:
self._data = BitStore()
try:
self._set_dtype(dtype)
Expand Down Expand Up @@ -230,7 +230,7 @@ def astype(self, dtype: Union[str, Dtype]) -> Array:
new_array = self.__class__(dtype, self.tolist())
return new_array

def tolist(self) -> List[ElementType]:
def tolist(self) -> list[ElementType]:
return [self._dtype.read_fn(self.data, start=start)
for start in range(0, len(self.data) - self._dtype.length + 1, self._dtype.length)]

Expand Down Expand Up @@ -312,7 +312,7 @@ def reverse(self) -> None:
raise ValueError(f"Cannot reverse the items in the Array as its data length ({len(self.data)} bits) is not a multiple of the format length ({self._dtype.length} bits).")
self.data = Bits.join([self.data[s - self._dtype.length: s] for s in range(len(self.data), 0, -self._dtype.length)])

def pp(self, fmt: Optional[str] = None, width: int = 120,
def pp(self, fmt: str | None = None, width: int = 120,
show_offset: bool = True, stream: TextIO = sys.stdout) -> None:
"""Pretty-print the Array contents.
Expand Down
90 changes: 43 additions & 47 deletions bitformat/bits.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,15 @@
import struct
import io
from collections import abc
from typing import Tuple, Union, List, Iterable, Any, Optional, TextIO, overload, Iterator, Type, TypeVar
from typing import Union, Iterable, Any, TextIO, overload, Iterator, Type, TypeVar
import bitformat
from .bitstore import BitStore
from bitformat import bitstore_helpers, utils
from bitformat.dtypes import Dtype, dtype_register
from bitformat.bitstring_options import Colour

# Things that can be converted to Bits when a Bits type is needed
BitsType = Union['Bits', str, Iterable[Any], bool, bytearray, bytes, memoryview]
BitsType = Union['Bits', str, Iterable[Any], bool, bytearray, bytes, memoryview, io.BytesIO]

TBits = TypeVar("TBits", bound='Bits')

Expand Down Expand Up @@ -62,7 +62,7 @@ def build(cls, dtype: Dtype | str, value: Any, /) -> TBits:
return d.build(value)

@classmethod
def fromstring(cls: TBits, s: str, /) -> TBits:
def fromstring(cls, s: str, /) -> TBits:
"""Create a new bitstring from a formatted string."""
x = super().__new__(cls)
x._bitstore = bitstore_helpers.str_to_bitstore(s)
Expand Down Expand Up @@ -104,7 +104,19 @@ def _create_from_bitstype(cls: Type[TBits], auto: BitsType, /) -> TBits:
if isinstance(auto, cls):
return auto
b = super().__new__(cls)
b._setauto_no_length_or_offset(auto)
if isinstance(auto, str):
b._bitstore = bitstore_helpers.str_to_bitstore(auto)
elif isinstance(auto, Bits):
b._bitstore = auto._bitstore.copy()
elif isinstance(auto, (bytes, bytearray, memoryview)):
b._bitstore = BitStore.frombytes(bytearray(auto))
elif isinstance(auto, io.BytesIO):
b._bitstore = BitStore.frombytes(auto.getvalue())
elif isinstance(auto, abc.Iterable):
# Evaluate each item as True or False and set bits to 1 or 0.
b._setbin_unsafe(''.join(str(int(bool(x))) for x in auto))
else:
raise TypeError(f"Cannot initialise Bits from type '{type(auto)}'.")
return b

def __iter__(self) -> Iterable[bool]:
Expand Down Expand Up @@ -204,7 +216,7 @@ def __str__(self) -> str:
def _repr(self, classname: str, length: int):
if length == 0:
s = ''
if length % 4 == 0:
elif length % 4 == 0:
s = '0x' + self.parse('hex')
else:
s = '0b' + self.parse('bin')
Expand All @@ -221,7 +233,7 @@ def __repr__(self) -> str:
def __eq__(self, bs: Any, /) -> bool:
"""Return True if two bitstrings have the same binary representation.
>>> Bits.fromstring('0b1110') == '0xe'
>>> Bits('0b1110') == '0xe'
True
"""
Expand All @@ -233,7 +245,7 @@ def __eq__(self, bs: Any, /) -> bool:
def __ne__(self, bs: Any, /) -> bool:
"""Return False if two bitstrings have the same binary representation.
>>> Bits.fromstring('0b111') == '0x7'
>>> Bits('0b111') == '0x7'
False
"""
Expand Down Expand Up @@ -339,27 +351,11 @@ def __bool__(self) -> bool:
"""Return False if bitstring is empty, otherwise return True."""
return len(self) != 0

def _setauto_no_length_or_offset(self, s: BitsType, /) -> None:
"""Set Bits from a Bits, bytes, iterable or string."""
if isinstance(s, str):
self._bitstore = bitstore_helpers.str_to_bitstore(s)
elif isinstance(s, Bits):
self._bitstore = s._bitstore.copy()
elif isinstance(s, (bytes, bytearray, memoryview)):
self._bitstore = BitStore.frombytes(bytearray(s))
elif isinstance(s, io.BytesIO):
self._bitstore = BitStore.frombytes(s.getvalue())
elif isinstance(s, abc.Iterable):
# Evaluate each item as True or False and set bits to 1 or 0.
self._setbin_unsafe(''.join(str(int(bool(x))) for x in s))
else:
raise TypeError(f"Cannot initialise bitstring from type '{type(s)}'.")

def _setbits(self, bs: BitsType, length: None = None) -> None:
bs = Bits._create_from_bitstype(bs)
self._bitstore = bs._bitstore

def _setbytes(self, data: Union[bytearray, bytes, List], length: None = None) -> None:
def _setbytes(self, data: Union[bytearray, bytes, list], length: None = None) -> None:
"""Set the data from a bytes or bytearray object."""
self._bitstore = BitStore.frombytes(bytes(data))

Expand All @@ -379,7 +375,7 @@ def _getbytes_printable(self) -> str:
string = ''.join(chr(0x100 + x) if x in Bits._unprintable else chr(x) for x in bytes_)
return string

def _setuint(self, uint: int, length: Optional[int] = None) -> None:
def _setuint(self, uint: int, length: int | None = None) -> None:
"""Reset the bitstring to have given unsigned int interpretation."""
# If no length given, and we've previously been given a length, use it.
if length is None and hasattr(self, 'len') and len(self) != 0:
Expand All @@ -394,7 +390,7 @@ def _getuint(self) -> int:
raise bitformat.InterpretError("Cannot interpret a zero length bitstring as an integer.")
return self._bitstore.slice_to_uint()

def _setint(self, int_: int, length: Optional[int] = None) -> None:
def _setint(self, int_: int, length: int | None = None) -> None:
"""Reset the bitstring to have given signed int interpretation."""
# If no length given, and we've previously been given a length, use it.
if length is None and hasattr(self, 'len') and len(self) != 0:
Expand All @@ -409,7 +405,7 @@ def _getint(self) -> int:
raise bitformat.InterpretError("Cannot interpret bitstring without a length as an integer.")
return self._bitstore.slice_to_int()

def _setfloat(self, f: float, length: Optional[int]) -> None:
def _setfloat(self, f: float, length: int | None) -> None:
if length is None and hasattr(self, 'len') and len(self) != 0:
length = len(self)
if length is None or length not in [16, 32, 64]:
Expand Down Expand Up @@ -496,16 +492,16 @@ def _addright(self, bs: Bits, /) -> None:
def _getbits(self: TBits):
return self._copy()

def _validate_slice(self, start: Optional[int], end: Optional[int]) -> Tuple[int, int]:
def _validate_slice(self, start: int | None, end: int | None) -> tuple[int, int]:
"""Validate start and end and return them as positive bit positions."""
start = 0 if start is None else (start + len(self) if start < 0 else start)
end = len(self) if end is None else (end + len(self) if end < 0 else end)
if not 0 <= start <= end <= len(self):
raise ValueError(f"Invalid slice positions for bitstring length {len(self)}: start={start}, end={end}.")
return start, end

def find(self, bs: BitsType, /, start: Optional[int] = None, end: Optional[int] = None,
bytealigned: Optional[bool] = None) -> Union[Tuple[int], Tuple[()]]:
def find(self, bs: BitsType, /, start: int | None = None, end: int | None = None,
bytealigned: bool | None = None) -> Union[tuple[int], tuple[()]]:
"""Find first occurrence of substring bs.
Returns a single item tuple with the bit position if found, or an
Expand Down Expand Up @@ -534,13 +530,13 @@ def find(self, bs: BitsType, /, start: Optional[int] = None, end: Optional[int]
p = self._find(bs, start, end, ba)
return p

def _find(self, bs: Bits, start: int, end: int, bytealigned: bool) -> Union[Tuple[int], Tuple[()]]:
def _find(self, bs: Bits, start: int, end: int, bytealigned: bool) -> Union[tuple[int], tuple[()]]:
"""Find first occurrence of a binary string."""
p = self._bitstore.find(bs._bitstore, start, end, bytealigned)
return () if p == -1 else (p,)

def findall(self, bs: BitsType, start: Optional[int] = None, end: Optional[int] = None, count: Optional[int] = None,
bytealigned: Optional[bool] = None) -> Iterable[int]:
def findall(self, bs: BitsType, start: int| None = None, end: int | None = None, count: int | None = None,
bytealigned: bool | None = None) -> Iterable[int]:
"""Find all occurrences of bs. Return generator of bit positions.
bs -- The bitstring to find.
Expand All @@ -564,7 +560,7 @@ def findall(self, bs: BitsType, start: Optional[int] = None, end: Optional[int]
ba = bitformat.options.bytealigned if bytealigned is None else bytealigned
return self._findall(bs, start, end, count, ba)

def _findall(self, bs: Bits, start: int, end: int, count: Optional[int],
def _findall(self, bs: Bits, start: int, end: int, count: int | None,
bytealigned: bool) -> Iterable[int]:
c = 0
for i in self._bitstore.findall(bs._bitstore, start, end, bytealigned):
Expand All @@ -574,8 +570,8 @@ def _findall(self, bs: Bits, start: int, end: int, count: Optional[int],
yield i
return

def rfind(self, bs: BitsType, /, start: Optional[int] = None, end: Optional[int] = None,
bytealigned: Optional[bool] = None) -> Union[Tuple[int], Tuple[()]]:
def rfind(self, bs: BitsType, /, start: int | None = None, end: int | None = None,
bytealigned: bool | None = None) -> Union[tuple[int], tuple[()]]:
"""Find final occurrence of substring bs.
Returns a single item tuple with the bit position if found, or an
Expand All @@ -601,13 +597,13 @@ def rfind(self, bs: BitsType, /, start: Optional[int] = None, end: Optional[int]
p = self._rfind(bs, start, end, ba)
return p

def _rfind(self, bs: Bits, start: int, end: int, bytealigned: bool) -> Union[Tuple[int], Tuple[()]]:
def _rfind(self, bs: Bits, start: int, end: int, bytealigned: bool) -> Union[tuple[int], tuple[()]]:
"""Find final occurrence of a binary string."""
p = self._bitstore.rfind(bs._bitstore, start, end, bytealigned)
return () if p == -1 else (p,)

def cut(self, bits: int, start: Optional[int] = None, end: Optional[int] = None,
count: Optional[int] = None) -> Iterator[Bits]:
def cut(self, bits: int, start: int | None = None, end: int | None = None,
count: int | None = None) -> Iterator[Bits]:
"""Return bitstring generator by cutting into bits sized chunks.
bits -- The size in bits of the bitstring chunks to generate.
Expand Down Expand Up @@ -643,7 +639,7 @@ def tobytes(self) -> bytes:
"""
return self._bitstore.tobytes()

def startswith(self, prefix: BitsType, start: Optional[int] = None, end: Optional[int] = None) -> bool:
def startswith(self, prefix: BitsType, start: int | None = None, end: int | None = None) -> bool:
"""Return whether the current bitstring starts with prefix.
prefix -- The bitstring to search for.
Expand All @@ -655,7 +651,7 @@ def startswith(self, prefix: BitsType, start: Optional[int] = None, end: Optiona
start, end = self._validate_slice(start, end)
return self._slice(start, start + len(prefix)) == prefix if end >= start + len(prefix) else False

def endswith(self, suffix: BitsType, start: Optional[int] = None, end: Optional[int] = None) -> bool:
def endswith(self, suffix: BitsType, start: int | None = None, end: int | None = None) -> bool:
"""Return whether the current bitstring ends with suffix.
suffix -- The bitstring to search for.
Expand All @@ -667,7 +663,7 @@ def endswith(self, suffix: BitsType, start: Optional[int] = None, end: Optional[
start, end = self._validate_slice(start, end)
return self._slice(end - len(suffix), end) == suffix if start + len(suffix) <= end else False

def all(self, value: Any, pos: Optional[Iterable[int]] = None) -> bool:
def all(self, value: Any, pos: Iterable[int] | None = None) -> bool:
"""Return True if one or many bits are all set to bool(value).
value -- If value is True then checks for bits set to 1, otherwise
Expand All @@ -684,7 +680,7 @@ def all(self, value: Any, pos: Optional[Iterable[int]] = None) -> bool:
return False
return True

def any(self, value: Any, pos: Optional[Iterable[int]] = None) -> bool:
def any(self, value: Any, pos: Iterable[int] | None = None) -> bool:
"""Return True if any of one or many bits are set to bool(value).
value -- If value is True then checks for bits set to 1, otherwise
Expand Down Expand Up @@ -717,7 +713,7 @@ def count(self, value: Any) -> int:

@staticmethod
def _format_bits(bits: Bits, bits_per_group: int, sep: str, dtype: Dtype,
colour_start: str, colour_end: str, width: Optional[int] = None) -> Tuple[str, int]:
colour_start: str, colour_end: str, width: int | None = None) -> tuple[str, int]:
get_fn = dtype.get_fn
if dtype.name == 'bytes': # Special case for bytes to print one character each.
get_fn = Bits._getbytes_printable
Expand All @@ -740,7 +736,7 @@ def _format_bits(bits: Bits, bits_per_group: int, sep: str, dtype: Dtype,
return x, chars_used

@staticmethod
def _chars_per_group(bits_per_group: int, fmt: Optional[str]):
def _chars_per_group(bits_per_group: int, fmt: str | None):
"""How many characters are needed to represent a number of bits with a given format."""
if fmt is None or dtype_register[fmt].bitlength2chars_fn is None:
return 0
Expand All @@ -753,7 +749,7 @@ def _bits_per_char(fmt: str):
raise ValueError
return 24 // dtype_register[fmt].bitlength2chars_fn(24)

def _pp(self, dtype1: Dtype, dtype2: Optional[Dtype], bits_per_group: int, width: int, sep: str, format_sep: str,
def _pp(self, dtype1: Dtype, dtype2: Dtype | None, bits_per_group: int, width: int, sep: str, format_sep: str,
show_offset: bool, stream: TextIO, offset_factor: int) -> None:
"""Internal pretty print method."""
colour = Colour(not bitformat.options.no_color)
Expand Down Expand Up @@ -850,7 +846,7 @@ def _process_pp_tokens(token_list, fmt):
bits_per_group //= 2
return dtype1, dtype2, bits_per_group, has_length_in_fmt

def pp(self, fmt: Optional[str] = None, width: int = 120, sep: str = ' ',
def pp(self, fmt: str | None = None, width: int = 120, sep: str = ' ',
show_offset: bool = True, stream: TextIO = sys.stdout) -> None:
"""Pretty print the bitstring's value.
Expand Down
16 changes: 8 additions & 8 deletions bitformat/bitstore.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import bitarray
import copy
from typing import Union, Iterable, Optional, Iterator, Any
from typing import Union, Iterable, Iterator, Any


class BitStore:
Expand Down Expand Up @@ -44,19 +44,19 @@ def setall(self, value: int, /) -> None:
def tobytes(self) -> bytes:
return self._bitarray.tobytes()

def slice_to_uint(self, start: Optional[int] = None, end: Optional[int] = None) -> int:
def slice_to_uint(self, start: int | None = None, end: int | None = None) -> int:
return bitarray.util.ba2int(self.getslice(start, end)._bitarray, signed=False)

def slice_to_int(self, start: Optional[int] = None, end: Optional[int] = None) -> int:
def slice_to_int(self, start: int | None = None, end: int | None = None) -> int:
return bitarray.util.ba2int(self.getslice(start, end)._bitarray, signed=True)

def slice_to_hex(self, start: Optional[int] = None, end: Optional[int] = None) -> str:
def slice_to_hex(self, start: int | None = None, end: int | None = None) -> str:
return bitarray.util.ba2hex(self.getslice(start, end)._bitarray)

def slice_to_bin(self, start: Optional[int] = None, end: Optional[int] = None) -> str:
def slice_to_bin(self, start: int | None = None, end: int | None = None) -> str:
return self.getslice(start, end)._bitarray.to01()

def slice_to_oct(self, start: Optional[int] = None, end: Optional[int] = None) -> str:
def slice_to_oct(self, start: int | None = None, end: int | None = None) -> str:
return bitarray.util.ba2base(8, self.getslice(start, end)._bitarray)

def __iadd__(self, other: BitStore, /) -> BitStore:
Expand Down Expand Up @@ -178,10 +178,10 @@ def getindex(self, index: int, /) -> bool:
def getslice_withstep(self, key: slice, /) -> BitStore:
return BitStore.frombitarray(self._bitarray.__getitem__(key))

def getslice(self, start: Optional[int], stop: Optional[int], /) -> BitStore:
def getslice(self, start: int | None, stop: int | None, /) -> BitStore:
return BitStore.frombitarray(self._bitarray[start:stop])

def invert(self, index: Optional[int] = None, /) -> None:
def invert(self, index: int | None = None, /) -> None:
if index is not None:
self._bitarray.invert(index)
else:
Expand Down
4 changes: 2 additions & 2 deletions bitformat/bitstore_helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import struct
import functools
from typing import Union, Optional, Dict, Callable
from typing import Union, Dict, Callable
import bitarray
from bitformat.bitstore import BitStore
import bitformat
Expand Down Expand Up @@ -103,7 +103,7 @@ def float2bitstore(f: Union[str, float], length: int) -> BitStore:
}


def bitstore_from_token(name: str, token_length: Optional[int], value: Optional[str]) -> BitStore:
def bitstore_from_token(name: str, token_length: int | None, value: str | None) -> BitStore:
if name in literal_bit_funcs:
return literal_bit_funcs[name](value)
try:
Expand Down
Loading

0 comments on commit ac09f13

Please sign in to comment.