Skip to content

Commit

Permalink
Removing the unknowable_length and always_fixed_length constants.
Browse files Browse the repository at this point in the history
They can now be checked at run time which is simpler and more flexible.
  • Loading branch information
scott-griffiths committed Dec 26, 2023
1 parent 90dbcb2 commit 25c3d71
Show file tree
Hide file tree
Showing 6 changed files with 30 additions and 70 deletions.
6 changes: 0 additions & 6 deletions bitstring/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,6 @@
from .dtypes import MetaDtype, dtype_register, Dtype
import types
from typing import List, Tuple, Literal
from .utils import initialise_constants


# We initialise the Options singleton after the base classes have been created.
Expand Down Expand Up @@ -263,11 +262,6 @@ def bool_bits2chars(bitlength: Literal[1]):
for alias in aliases:
dtype_register.add_meta_dtype_alias(alias[0], alias[1])

unknowable_length_names = dtype_register.unknowable_length_names()
always_fixed_length = dtype_register.always_fixed_length()

initialise_constants(unknowable_length_names, always_fixed_length)

__all__ = ['ConstBitStream', 'BitStream', 'BitArray', 'Array',
'Bits', 'pack', 'Error', 'ReadError', 'InterpretError',
'ByteAlignError', 'CreationError', 'bytealigned', 'lsb0', 'Dtype']
27 changes: 14 additions & 13 deletions bitstring/array_.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
from bitstring.bits import Bits, BitsType
from bitstring.bitarray_ import BitArray
from bitstring.dtypes import Dtype, dtype_register
from bitstring.utils import tokenparser, parse_name_length_token, parse_single_struct_token
from bitstring.utils import parse_name_length_token, parse_single_struct_token, preprocess_tokens
import copy
import array
import operator
Expand Down Expand Up @@ -377,24 +377,25 @@ def pp(self, fmt: Optional[str] = None, width: int = 120,
number_of_fmts = 1

if fmt is None:
token_name, token_length = self.dtype.name, self.dtype.length
dtypes = [self.dtype]
parameter_str = f"dtype='{self.dtype}'"
else:
tokens = tokenparser(fmt)[1]
token_names_and_lengths = [(x[0], x[1]) for x in tokens]
number_of_fmts = len(token_names_and_lengths)
if number_of_fmts not in [1, 2]:
raise ValueError(
f"Only one or two tokens can be used in an Array.pp() format - '{fmt}' has {number_of_fmts} tokens.")
token_name, token_length = token_names_and_lengths[0]
token_list = preprocess_tokens(fmt)
if len(token_list) not in [1, 2]:
raise ValueError(f"Only one or two tokens can be used in an Array.pp() format - '{fmt}' has {len(token_list)} tokens.")
dtypes = [Dtype(*parse_name_length_token(t)) for t in token_list]
parameter_str = f"fmt='{fmt}'"

token_name2, token_length2 = None, None
if number_of_fmts == 1:
token_name, token_length = dtypes[0].name, dtypes[0].bitlength
token_name2 = dtypes[1].name if len(dtypes) > 1 else None
if len(dtypes) == 1:
# Only one type, so use its length, or if not present the current itemsize
if token_length is None:
token_length = self.itemsize
if number_of_fmts == 2:
token_name2, token_length2 = token_names_and_lengths[1]
else:
# For two types we're OK as long as they don't have different lengths given.
assert len(dtypes) == 2
token_length2 = dtypes[1].bitlength
if token_length is None and token_length2 is None:
token_length = token_length2 = self.itemsize
if token_length is None:
Expand Down
7 changes: 4 additions & 3 deletions bitstring/bits.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,16 +38,17 @@
options = None

def _initialise_bits_class() -> None:
from .dtypes import dtype_register as _dtype_register
from bitstring.dtypes import dtype_register as _dtype_register
global dtype_register
dtype_register = _dtype_register
from .dtypes import Dtype as _Dtype
from bitstring.dtypes import Dtype as _Dtype
global Dtype
Dtype = _Dtype
from .options import Options
from bitstring.options import Options
global options
options = Options()


class Bits:
"""A container holding an immutable sequence of bits.
Expand Down
20 changes: 10 additions & 10 deletions bitstring/dtypes.py
Original file line number Diff line number Diff line change
Expand Up @@ -101,22 +101,22 @@ def __init__(self, name: str, description: str, set_fn, read_fn, get_fn, return_

self.bitlength2chars_fn = bitlength2chars_fn


def getDtype(self, length: Optional[int] = None) -> Dtype:
if length is None:
d = Dtype.create(self, None)
return d
if self.is_unknown_length:
raise ValueError("Length shouldn't be supplied for dtypes that are variable length.")
if self.fixed_length:
if length == 0:
if length is None:
if len(self.fixed_length) == 1:
length = self.fixed_length[0]
else:
raise ValueError # TODO
else:
if length not in self.fixed_length:
raise ValueError # TODO
if len(self.fixed_length) == 1:
raise ValueError(f"A length of {length} was supplied for the '{self.name}' dtype, but its only allowed length is {self.fixed_length[0]}.")
else:
raise ValueError(f"A length of {length} was supplied for the '{self.name}' dtype which is not one of its possible lengths (must be one of {self.fixed_length}).")
if length is None:
d = Dtype.create(self, None)
return d
if self.is_unknown_length:
raise ValueError("Length shouldn't be supplied for dtypes that are variable length.")
d = Dtype.create(self, length)
return d

Expand Down
38 changes: 2 additions & 36 deletions bitstring/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,18 +12,6 @@
# A token name followed by optional : then an arbitrary keyword
NAME_KWARG_RE: Pattern[str] = re.compile(r'^([a-zA-Z][a-zA-Z0-9_]*?):?([a-zA-Z0-9_]+)$')

# Tokens which have an unknowable (in advance) length, so it must not be supplied.
UNKNOWABLE_LENGTH_TOKENS: Tuple[str] = ()

# Tokens which are always the same length, so it doesn't need to be supplied.
ALWAYS_FIXED_LENGTH_TOKENS: Dict[str, int] = None

def initialise_constants(unknowable_length_names: Tuple[str], always_fixed_length: Dict[str, int]) -> None:
global UNKNOWABLE_LENGTH_TOKENS, ALWAYS_FIXED_LENGTH_TOKENS
UNKNOWABLE_LENGTH_TOKENS = unknowable_length_names
ALWAYS_FIXED_LENGTH_TOKENS = always_fixed_length


CACHE_SIZE = 256

DEFAULT_BITS: Pattern[str] = re.compile(r'^(?P<len>[^=]+)?(=(?P<value>.*))?$', re.IGNORECASE)
Expand Down Expand Up @@ -105,17 +93,6 @@ def parse_name_length_token(fmt: str, **kwargs) -> Tuple[str, Optional[int]]:
length = int(length_str)
else:
raise ValueError(f"Can't parse 'name[:]length' token '{fmt}'.")

if name in UNKNOWABLE_LENGTH_TOKENS:
if length is not None:
raise ValueError(
f"The token '{name}' has a variable length and can't be given the fixed length of {length}.")

if name in ALWAYS_FIXED_LENGTH_TOKENS.keys():
token_length = ALWAYS_FIXED_LENGTH_TOKENS[name]
if length not in [None, token_length]:
raise ValueError(f"{name} tokens can only be {token_length} bits long, not {length} bits.")
length = token_length
return name, length


Expand Down Expand Up @@ -155,13 +132,6 @@ def parse_single_token(token: str) -> Tuple[str, str, Optional[str]]:
# If you don't specify a 'name' then the default is 'bits'
name = 'bits'
length = token

if name in ALWAYS_FIXED_LENGTH_TOKENS.keys():
token_length = str(ALWAYS_FIXED_LENGTH_TOKENS[name])
if length is not None and length != token_length:
raise ValueError(f"{name} tokens can only be {token_length} bits long, not {length} bits.")
length = token_length

return name, length, value

def preprocess_tokens(fmt: str) -> List[str]:
Expand Down Expand Up @@ -219,12 +189,8 @@ def tokenparser(fmt: str, keys: Tuple[str, ...] = ()) -> \
ret_vals.append((m.group('name'), None, m.group('value')))
continue
name, length, value = parse_single_token(token)
if name in UNKNOWABLE_LENGTH_TOKENS:
if length is not None:
raise ValueError(f"The token '{name}' has a variable length and can't be given the fixed length of {length}.")
else:
if length is None:
stretchy_token = True
if length is None:
stretchy_token = True
if length is not None:
# Try converting length to int, otherwise check it's a key.
try:
Expand Down
2 changes: 0 additions & 2 deletions tests/test_bitstream.py
Original file line number Diff line number Diff line change
Expand Up @@ -2353,8 +2353,6 @@ def testTokenParser(self):
tp = bitstring.utils.tokenparser
self.assertEqual(tp('hex'), (True, [('hex', None, None)]))
self.assertEqual(tp('hex=14'), (True, [('hex', None, '14')]))
self.assertEqual(tp('se'), (False, [('se', None, None)]))
self.assertEqual(tp('ue=12'), (False, [('ue', None, '12')]))
self.assertEqual(tp('0xef'), (False, [('0x', None, 'ef')]))
self.assertEqual(tp('uint:12'), (False, [('uint', 12, None)]))
self.assertEqual(tp('int:30=-1'), (False, [('int', 30, '-1')]))
Expand Down

0 comments on commit 25c3d71

Please sign in to comment.