bitstring 4.2.0b1__tar.gz → 4.2.0b2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) hide show
  1. {bitstring-4.2.0b1/bitstring.egg-info → bitstring-4.2.0b2}/PKG-INFO +15 -14
  2. {bitstring-4.2.0b1 → bitstring-4.2.0b2}/README.md +14 -13
  3. {bitstring-4.2.0b1 → bitstring-4.2.0b2}/bitstring/__init__.py +60 -11
  4. {bitstring-4.2.0b1 → bitstring-4.2.0b2}/bitstring/array_.py +77 -44
  5. {bitstring-4.2.0b1 → bitstring-4.2.0b2}/bitstring/bits.py +205 -197
  6. {bitstring-4.2.0b1 → bitstring-4.2.0b2}/bitstring/bitstore.py +35 -28
  7. bitstring-4.2.0b2/bitstring/bitstore_helpers.py +260 -0
  8. {bitstring-4.2.0b1 → bitstring-4.2.0b2}/bitstring/bitstream.py +19 -2
  9. bitstring-4.2.0b2/bitstring/bitstring_options.py +94 -0
  10. bitstring-4.2.0b2/bitstring/dtypes.py +397 -0
  11. bitstring-4.2.0b2/bitstring/fp8.py +97 -0
  12. bitstring-4.2.0b2/bitstring/luts.py +248 -0
  13. {bitstring-4.2.0b1 → bitstring-4.2.0b2}/bitstring/methods.py +2 -5
  14. bitstring-4.2.0b2/bitstring/mxfp.py +200 -0
  15. {bitstring-4.2.0b1 → bitstring-4.2.0b2}/bitstring/utils.py +20 -24
  16. {bitstring-4.2.0b1 → bitstring-4.2.0b2/bitstring.egg-info}/PKG-INFO +15 -14
  17. {bitstring-4.2.0b1 → bitstring-4.2.0b2}/bitstring.egg-info/SOURCES.txt +5 -1
  18. {bitstring-4.2.0b1 → bitstring-4.2.0b2}/pyproject.toml +1 -1
  19. {bitstring-4.2.0b1 → bitstring-4.2.0b2}/release_notes.txt +36 -9
  20. bitstring-4.2.0b2/tests/test_array.py +929 -0
  21. bitstring-4.2.0b2/tests/test_bitarray.py +979 -0
  22. bitstring-4.2.0b2/tests/test_bits.py +981 -0
  23. bitstring-4.2.0b2/tests/test_bitstore.py +94 -0
  24. bitstring-4.2.0b2/tests/test_bitstream.py +4172 -0
  25. {bitstring-4.2.0b1 → bitstring-4.2.0b2}/tests/test_bitstring.py +73 -74
  26. bitstring-4.2.0b2/tests/test_constbitstream.py +265 -0
  27. bitstring-4.2.0b2/tests/test_dtypes.py +134 -0
  28. bitstring-4.2.0b2/tests/test_fp8.py +295 -0
  29. bitstring-4.2.0b2/tests/test_mxfp.py +441 -0
  30. bitstring-4.2.0b2/tests/test_scaled_dtypes.py +13 -0
  31. bitstring-4.2.0b1/bitstring/bitstore_helpers.py +0 -252
  32. bitstring-4.2.0b1/bitstring/bitstring_options.py +0 -89
  33. bitstring-4.2.0b1/bitstring/dtypes.py +0 -244
  34. bitstring-4.2.0b1/bitstring/fp8.py +0 -93
  35. bitstring-4.2.0b1/tests/test_array.py +0 -915
  36. bitstring-4.2.0b1/tests/test_bitarray.py +0 -979
  37. bitstring-4.2.0b1/tests/test_bits.py +0 -974
  38. bitstring-4.2.0b1/tests/test_bitstore.py +0 -94
  39. bitstring-4.2.0b1/tests/test_bitstream.py +0 -4174
  40. bitstring-4.2.0b1/tests/test_constbitstream.py +0 -247
  41. bitstring-4.2.0b1/tests/test_dtypes.py +0 -86
  42. bitstring-4.2.0b1/tests/test_fp8.py +0 -220
  43. {bitstring-4.2.0b1 → bitstring-4.2.0b2}/LICENSE +0 -0
  44. {bitstring-4.2.0b1 → bitstring-4.2.0b2}/MANIFEST.in +0 -0
  45. {bitstring-4.2.0b1 → bitstring-4.2.0b2}/bitstring/__main__.py +0 -0
  46. {bitstring-4.2.0b1 → bitstring-4.2.0b2}/bitstring/bitarray_.py +0 -0
  47. {bitstring-4.2.0b1 → bitstring-4.2.0b2}/bitstring/exceptions.py +0 -0
  48. {bitstring-4.2.0b1 → bitstring-4.2.0b2}/bitstring/py.typed +0 -0
  49. {bitstring-4.2.0b1 → bitstring-4.2.0b2}/bitstring.egg-info/dependency_links.txt +0 -0
  50. {bitstring-4.2.0b1 → bitstring-4.2.0b2}/bitstring.egg-info/requires.txt +0 -0
  51. {bitstring-4.2.0b1 → bitstring-4.2.0b2}/bitstring.egg-info/top_level.txt +0 -0
  52. {bitstring-4.2.0b1 → bitstring-4.2.0b2}/setup.cfg +0 -0
  53. {bitstring-4.2.0b1 → bitstring-4.2.0b2}/tests/__init__.py +0 -0
  54. {bitstring-4.2.0b1 → bitstring-4.2.0b2}/tests/smalltestfile +0 -0
  55. {bitstring-4.2.0b1 → bitstring-4.2.0b2}/tests/test.m1v +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: bitstring
3
- Version: 4.2.0b1
3
+ Version: 4.2.0b2
4
4
  Summary: Simple construction, analysis and modification of binary data.
5
5
  Author-email: Scott Griffiths <dr.scottgriffiths@gmail.com>
6
6
  Project-URL: homepage, https://github.com/scott-griffiths/bitstring
@@ -37,17 +37,25 @@ It has been actively maintained since 2006.
37
37
  [![Codacy Badge](https://img.shields.io/codacy/grade/8869499b2eed44548fa1a5149dd451f4?logo=codacy)](https://app.codacy.com/gh/scott-griffiths/bitstring/dashboard?utm_source=gh&utm_medium=referral&utm_content=&utm_campaign=Badge_grade)
38
38
  [![Dependents (via libraries.io)](https://img.shields.io/librariesio/dependents/pypi/bitstring?logo=libraries.io&logoColor=white)](https://libraries.io/pypi/bitstring)
39
39
  &nbsp; &nbsp;
40
- [![Pepy Total Downlods](https://img.shields.io/pepy/dt/bitstring?logo=python&logoColor=white&labelColor=blue&color=blue)](https://pypistats.org/packages/bitstring)
40
+ [![Pepy Total Downlods](https://img.shields.io/pepy/dt/bitstring?logo=python&logoColor=white&labelColor=blue&color=blue)](https://www.pepy.tech/projects/bitstring)
41
41
  [![PyPI - Downloads](https://img.shields.io/pypi/dm/bitstring?label=%40&labelColor=blue&color=blue)](https://pypistats.org/packages/bitstring)
42
42
 
43
43
 
44
44
  News
45
45
  ----
46
- **29th November 2023**: bitstring 4.1.4 released. Version 4.1 is a large update in terms of how much of the code has changed.
46
+ **March 2024**: bitstring 4.2.0b2 released. This is the final beta release before 4.2.
47
47
 
48
- * New Array class for homogeneous data.
49
- * Support for 8-bit floating point values.
50
- * Speed increased with bitarray dependency.
48
+ Install the beta with `pip install bitstring==4.2.0b2`, or use `pip install bitstring` for the latest stable version.
49
+
50
+ New in 4.2:
51
+
52
+ * Dropped support for Python 3.7. Minimum version is now 3.8.
53
+ * A new `Dtype` class can be optionally used to specify types.
54
+ * The `bitstring.options` object is now the preferred method for changing module options.
55
+ * New `fromstring` method as another way to create bitstrings from formatted strings.
56
+ * More types can now be pretty printed.
57
+ * A range of 8-bit, 6-bit and even 4-bit float formats added (beta):
58
+ * Performance improvements.
51
59
 
52
60
  See the [release notes](https://github.com/scott-griffiths/bitstring/blob/main/release_notes.txt) for details. Please let me know if you encounter any problems.
53
61
 
@@ -137,13 +145,6 @@ Examples
137
145
  Array('uint7', [45, 100, 15, 1])
138
146
 
139
147
 
140
- Unit Tests
141
- ----------
142
-
143
- The 700+ unit tests should all pass. They can be run from the root of the project with
144
-
145
- python -m unittest
146
-
147
148
 
148
149
  Credits
149
150
  -------
@@ -153,4 +154,4 @@ Maintained and expanded ever since as it became unexpectedly popular.
153
154
  Thanks to all those who have contributed ideas and code (and bug reports) over the years.
154
155
 
155
156
 
156
- <sub>Copyright (c) 2006 - 2023 Scott Griffiths</sub>
157
+ <sub>Copyright (c) 2006 - 2024 Scott Griffiths</sub>
@@ -13,17 +13,25 @@ It has been actively maintained since 2006.
13
13
  [![Codacy Badge](https://img.shields.io/codacy/grade/8869499b2eed44548fa1a5149dd451f4?logo=codacy)](https://app.codacy.com/gh/scott-griffiths/bitstring/dashboard?utm_source=gh&utm_medium=referral&utm_content=&utm_campaign=Badge_grade)
14
14
  [![Dependents (via libraries.io)](https://img.shields.io/librariesio/dependents/pypi/bitstring?logo=libraries.io&logoColor=white)](https://libraries.io/pypi/bitstring)
15
15
  &nbsp; &nbsp;
16
- [![Pepy Total Downlods](https://img.shields.io/pepy/dt/bitstring?logo=python&logoColor=white&labelColor=blue&color=blue)](https://pypistats.org/packages/bitstring)
16
+ [![Pepy Total Downlods](https://img.shields.io/pepy/dt/bitstring?logo=python&logoColor=white&labelColor=blue&color=blue)](https://www.pepy.tech/projects/bitstring)
17
17
  [![PyPI - Downloads](https://img.shields.io/pypi/dm/bitstring?label=%40&labelColor=blue&color=blue)](https://pypistats.org/packages/bitstring)
18
18
 
19
19
 
20
20
  News
21
21
  ----
22
- **29th November 2023**: bitstring 4.1.4 released. Version 4.1 is a large update in terms of how much of the code has changed.
22
+ **March 2024**: bitstring 4.2.0b2 released. This is the final beta release before 4.2.
23
23
 
24
- * New Array class for homogeneous data.
25
- * Support for 8-bit floating point values.
26
- * Speed increased with bitarray dependency.
24
+ Install the beta with `pip install bitstring==4.2.0b2`, or use `pip install bitstring` for the latest stable version.
25
+
26
+ New in 4.2:
27
+
28
+ * Dropped support for Python 3.7. Minimum version is now 3.8.
29
+ * A new `Dtype` class can be optionally used to specify types.
30
+ * The `bitstring.options` object is now the preferred method for changing module options.
31
+ * New `fromstring` method as another way to create bitstrings from formatted strings.
32
+ * More types can now be pretty printed.
33
+ * A range of 8-bit, 6-bit and even 4-bit float formats added (beta):
34
+ * Performance improvements.
27
35
 
28
36
  See the [release notes](https://github.com/scott-griffiths/bitstring/blob/main/release_notes.txt) for details. Please let me know if you encounter any problems.
29
37
 
@@ -113,13 +121,6 @@ Examples
113
121
  Array('uint7', [45, 100, 15, 1])
114
122
 
115
123
 
116
- Unit Tests
117
- ----------
118
-
119
- The 700+ unit tests should all pass. They can be run from the root of the project with
120
-
121
- python -m unittest
122
-
123
124
 
124
125
  Credits
125
126
  -------
@@ -129,4 +130,4 @@ Maintained and expanded ever since as it became unexpectedly popular.
129
130
  Thanks to all those who have contributed ideas and code (and bug reports) over the years.
130
131
 
131
132
 
132
- <sub>Copyright (c) 2006 - 2023 Scott Griffiths</sub>
133
+ <sub>Copyright (c) 2006 - 2024 Scott Griffiths</sub>
@@ -10,15 +10,15 @@ BitArray -- A mutable container for binary data.
10
10
  ConstBitStream -- An immutable container with streaming methods.
11
11
  BitStream -- A mutable container with streaming methods.
12
12
  Array -- An efficient list-like container where each item has a fixed-length binary format.
13
+ Dtype -- Encapsulate the data types used in the other classes.
13
14
 
14
15
  Functions:
15
16
 
16
17
  pack -- Create a BitStream from a format string.
17
18
 
18
19
  Data:
19
- options -- A container for module-wide options:
20
- bytealigned -- Determines whether a number of methods default to working only on byte boundaries.
21
- lsb0 -- If True, the least significant bit (the final bit) is indexed as bit zero.
20
+
21
+ options -- Module-wide options.
22
22
 
23
23
  Exceptions:
24
24
 
@@ -55,7 +55,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
55
55
  THE SOFTWARE.
56
56
  """
57
57
 
58
- __version__ = "4.2.0b1"
58
+ __version__ = "4.2.0b2"
59
59
 
60
60
  __author__ = "Scott Griffiths"
61
61
 
@@ -71,6 +71,12 @@ from .exceptions import Error, ReadError, InterpretError, ByteAlignError, Creati
71
71
  from .dtypes import DtypeDefinition, dtype_register, Dtype
72
72
  import types
73
73
  from typing import List, Tuple, Literal
74
+ from .mxfp import decompress_luts as mxfp_decompress_luts
75
+ from .fp8 import decompress_luts as binary8_decompress_luts
76
+
77
+ # Decompress the LUTs for the exotic floating point formats
78
+ mxfp_decompress_luts()
79
+ binary8_decompress_luts()
74
80
 
75
81
  # The Options class returns a singleton.
76
82
  options = Options()
@@ -136,13 +142,40 @@ def float_bits2chars(bitlength: Literal[16, 32, 64]):
136
142
  else:
137
143
  return 24 # Empirical value
138
144
 
139
- def e5m2float_bits2chars(bitlength: Literal[8]):
145
+ def p3binary_bits2chars(bitlength: Literal[8]):
140
146
  return 19 # Empirical value
141
147
 
142
- def e4m3float_bits2chars(bitlength: Literal[8]):
148
+ def p4binary_bits2chars(bitlength: Literal[8]):
143
149
  # Found by looking at all the possible values
144
150
  return 13 # Empirical value
145
151
 
152
+ def e4m3mxfp_bits2chars(bitlength: Literal[8]):
153
+ return 13
154
+
155
+ def e5m2mxfp_bits2chars(bitlength: Literal[8]):
156
+ return 19
157
+
158
+ def e3m2mxfp_bits2chars(bitlength: Literal[6]):
159
+ # Not sure what the best value is here. It's 7 without considering the scale that could be applied.
160
+ return 7
161
+
162
+ def e2m3mxfp_bits2chars(bitlength: Literal[6]):
163
+ # Not sure what the best value is here.
164
+ return 7
165
+
166
+ def e2m1mxfp_bits2chars(bitlength: Literal[4]):
167
+ # Not sure what the best value is here.
168
+ return 7
169
+
170
+ def e8m0mxfp_bits2chars(bitlength: Literal[8]):
171
+ # Can range same as float32
172
+ return 23
173
+
174
+ def mxint_bits2chars(bitlength: Literal[8]):
175
+ # Not sure what the best value is here.
176
+ return 10
177
+
178
+
146
179
  def bfloat_bits2chars(bitlength: Literal[16]):
147
180
  # Found by looking at all the possible values
148
181
  return 23 # Empirical value
@@ -175,13 +208,9 @@ dtype_definitions = [
175
208
  allowed_lengths=(0, 4, 8, ...), description="a hexadecimal string"),
176
209
  DtypeDefinition('bin', Bits._setbin_safe, Bits._getbin, str, False, bin_bits2chars,
177
210
  description="a binary string"),
178
- DtypeDefinition('oct',Bits._setoct, Bits._getoct, str, False, oct_bits2chars,
211
+ DtypeDefinition('oct', Bits._setoct, Bits._getoct, str, False, oct_bits2chars,
179
212
  allowed_lengths=(0, 3, 6, ...), description="an octal string"),
180
213
  # Float types
181
- DtypeDefinition('e5m2float', Bits._sete5m2float, Bits._gete5m2float, float, True, e5m2float_bits2chars,
182
- allowed_lengths=(8,), description="an 8 bit float with e5m2float format"),
183
- DtypeDefinition('e4m3float', Bits._sete4m3float, Bits._gete4m3float, float, True, e4m3float_bits2chars,
184
- allowed_lengths=(8,), description="an 8 bit float with e4m3float format"),
185
214
  DtypeDefinition('float', Bits._setfloatbe, Bits._getfloatbe, float, True, float_bits2chars,
186
215
  allowed_lengths=(16, 32, 64), description="a big-endian floating point number"),
187
216
  DtypeDefinition('floatle', Bits._setfloatle, Bits._getfloatle, float, True, float_bits2chars,
@@ -209,6 +238,26 @@ dtype_definitions = [
209
238
  # Special case pad type
210
239
  DtypeDefinition('pad', Bits._setpad, Bits._getpad, None, False, None,
211
240
  description="a skipped section of padding"),
241
+
242
+ # MXFP and IEEE 8-bit float types
243
+ DtypeDefinition('p3binary', Bits._setp3binary, Bits._getp3binary, float, True, p3binary_bits2chars,
244
+ allowed_lengths=(8,), description="an 8 bit float with binary8p3 format"),
245
+ DtypeDefinition('p4binary', Bits._setp4binary, Bits._getp4binary, float, True, p4binary_bits2chars,
246
+ allowed_lengths=(8,), description="an 8 bit float with binary8p4 format"),
247
+ DtypeDefinition('e4m3mxfp', Bits._sete4m3mxfp, Bits._gete4m3mxfp, float, True, e4m3mxfp_bits2chars,
248
+ allowed_lengths=(8,), description="an 8 bit float with MXFP E4M3 format"),
249
+ DtypeDefinition('e5m2mxfp', Bits._sete5m2mxfp, Bits._gete5m2mxfp, float, True, e5m2mxfp_bits2chars,
250
+ allowed_lengths=(8,), description="an 8 bit float with MXFP E5M2 format"),
251
+ DtypeDefinition('e3m2mxfp', Bits._sete3m2mxfp, Bits._gete3m2mxfp, float, True, e3m2mxfp_bits2chars,
252
+ allowed_lengths=(6,), description="a 6 bit float with MXFP E3M2 format"),
253
+ DtypeDefinition('e2m3mxfp', Bits._sete2m3mxfp, Bits._gete2m3mxfp, float, True, e2m3mxfp_bits2chars,
254
+ allowed_lengths=(6,), description="a 6 bit float with MXFP E2M3 format"),
255
+ DtypeDefinition('e2m1mxfp', Bits._sete2m1mxfp, Bits._gete2m1mxfp, float, True, e2m1mxfp_bits2chars,
256
+ allowed_lengths=(4,), description="a 4 bit float with MXFP E2M1 format"),
257
+ DtypeDefinition('e8m0mxfp', Bits._sete8m0mxfp, Bits._gete8m0mxfp, float, False, e8m0mxfp_bits2chars,
258
+ allowed_lengths=(8,), description="an 8 bit float with MXFP E8M0 format"),
259
+ DtypeDefinition('mxint', Bits._setmxint, Bits._getmxint, float, True, mxint_bits2chars,
260
+ allowed_lengths=(8,), description="an 8 bit float with MXFP INT8 format"),
212
261
  ]
213
262
 
214
263
 
@@ -70,6 +70,11 @@ class Array:
70
70
  def __init__(self, dtype: Union[str, Dtype], initializer: Optional[Union[int, Array, array.array, Iterable, Bits, bytes, bytearray, memoryview, BinaryIO]] = None,
71
71
  trailing_bits: Optional[BitsType] = None) -> None:
72
72
  self.data = BitArray()
73
+ if isinstance(dtype, Dtype) and dtype.scale == 'auto':
74
+ if isinstance(initializer, (int, Bits, bytes, bytearray, memoryview, BinaryIO)):
75
+ raise TypeError("An Array with an 'auto' scale factor can only be created from an iterable of values.")
76
+ auto_scale = self._calculate_auto_scale(initializer, dtype.name, dtype.length)
77
+ dtype = Dtype(dtype.name, dtype.length, scale=auto_scale)
73
78
  try:
74
79
  self._set_dtype(dtype)
75
80
  except ValueError as e:
@@ -87,6 +92,43 @@ class Array:
87
92
  if trailing_bits is not None:
88
93
  self.data += BitArray._create_from_bitstype(trailing_bits)
89
94
 
95
+ _largest_values = None
96
+ @staticmethod
97
+ def _calculate_auto_scale(initializer, name: str, length: Optional[int]) -> float:
98
+ # Now need to find the largest power of 2 representable with this format.
99
+ if Array._largest_values is None:
100
+ Array._largest_values = {
101
+ 'mxint8': Bits('0b01111111').mxint8, # 1.0 + 63.0/64.0,
102
+ 'e2m1mxfp4': Bits('0b0111').e2m1mxfp4, # 6.0
103
+ 'e2m3mxfp6': Bits('0b011111').e2m3mxfp6, # 7.5
104
+ 'e3m2mxfp6': Bits('0b011111').e3m2mxfp6, # 28.0
105
+ 'e4m3mxfp8': Bits('0b01111110').e4m3mxfp8, # 448.0
106
+ 'e5m2mxfp8': Bits('0b01111011').e5m2mxfp8, # 57344.0
107
+ 'p4binary8': Bits('0b01111110').p4binary8, # 224.0
108
+ 'p3binary8': Bits('0b01111110').p3binary8, # 49152.0
109
+ 'float16': Bits('0x7bff').float16, # 65504.0
110
+ # The bfloat range is so large the scaling algorithm doesn't work well, so I'm disallowing it.
111
+ # 'bfloat16': Bits('0x7f7f').bfloat16, # 3.38953139e38,
112
+ }
113
+ if f'{name}{length}' in Array._largest_values.keys():
114
+ float_values = Array('float64', initializer).tolist()
115
+ max_float_value = max(abs(x) for x in float_values)
116
+ if max_float_value == 0:
117
+ # This special case isn't covered in the standard. I'm choosing to return no scale.
118
+ return 1.0
119
+ log2 = int(math.log2(max_float_value))
120
+ lp2 = int(math.log2(Array._largest_values[f'{name}{length}']))
121
+ lg_scale = log2 - lp2
122
+ # Saturate at values representable in E8M0 format.
123
+ if lg_scale > 127:
124
+ lg_scale = 127
125
+ elif lg_scale < -127:
126
+ lg_scale = -127
127
+ return 2 ** lg_scale
128
+ else:
129
+ raise ValueError(f"Can't calculate auto scale for format '{name}{length}'. "
130
+ f"This feature is only available for these formats: {list(Array._largest_values.keys())}.")
131
+
90
132
  @property
91
133
  def itemsize(self) -> int:
92
134
  return self._dtype.length
@@ -96,19 +138,6 @@ class Array:
96
138
  trailing_bit_length = len(self.data) % self._dtype.bitlength
97
139
  return BitArray() if trailing_bit_length == 0 else self.data[-trailing_bit_length:]
98
140
 
99
- # Converting array.array typecodes to our equivalents.
100
- _array_typecodes: dict[str, str] = {'b': 'int8',
101
- 'B': 'uint8',
102
- 'h': 'intne16',
103
- 'H': 'uintne16',
104
- 'l': 'intne32',
105
- 'L': 'uintne32',
106
- 'q': 'intne64',
107
- 'Q': 'uintne64',
108
- 'e': 'floatne16',
109
- 'f': 'floatne32',
110
- 'd': 'floatne64'}
111
-
112
141
  @property
113
142
  def dtype(self) -> Dtype:
114
143
  return self._dtype
@@ -120,7 +149,6 @@ class Array:
120
149
  def _set_dtype(self, new_dtype: Union[str, Dtype]) -> None:
121
150
  if isinstance(new_dtype, Dtype):
122
151
  self._dtype = new_dtype
123
- self._fmt = str(self._dtype)
124
152
  else:
125
153
  try:
126
154
  dtype = Dtype(new_dtype)
@@ -133,14 +161,14 @@ class Array:
133
161
  if dtype.length is None:
134
162
  raise ValueError(f"A fixed length format is needed for an Array, received '{new_dtype}'.")
135
163
  self._dtype = dtype
136
- self._fmt = new_dtype
164
+ if self._dtype.scale == 'auto':
165
+ raise ValueError("A Dtype with an 'auto' scale factor can only be used when creating a new Array.")
137
166
 
138
167
  def _create_element(self, value: ElementType) -> Bits:
139
168
  """Create Bits from value according to the token_name and token_length"""
140
- b = Bits()
141
- self._dtype.set_fn(b, value)
169
+ b = self._dtype.build(value)
142
170
  if len(b) != self._dtype.length:
143
- raise ValueError(f"The value {value!r} has the wrong length for the format '{self._fmt}'.")
171
+ raise ValueError(f"The value {value!r} has the wrong length for the format '{self._dtype}'.")
144
172
  return b
145
173
 
146
174
  def __len__(self) -> int:
@@ -161,11 +189,11 @@ class Array:
161
189
  d = BitArray()
162
190
  for s in range(start * self._dtype.length, stop * self._dtype.length, step * self._dtype.length):
163
191
  d.append(self.data[s: s + self._dtype.length])
164
- a = Array(self._dtype)
192
+ a = self.__class__(self._dtype)
165
193
  a.data = d
166
194
  return a
167
195
  else:
168
- a = Array(self._dtype)
196
+ a = self.__class__(self._dtype)
169
197
  a.data = self.data[start * self._dtype.length: stop * self._dtype.length]
170
198
  return a
171
199
  else:
@@ -234,15 +262,15 @@ class Array:
234
262
  trailing_bit_length = len(self.data) % self._dtype.length
235
263
  final_str = "" if trailing_bit_length == 0 else ", trailing_bits=" + repr(
236
264
  self.data[-trailing_bit_length:])
237
- return f"Array('{self._fmt}', {list_str}{final_str})"
265
+ return f"Array('{self._dtype}', {list_str}{final_str})"
238
266
 
239
267
  def astype(self, dtype: Union[str, Dtype]) -> Array:
240
268
  """Return Array with elements of new dtype, initialised from current Array."""
241
- new_array = Array(dtype, self.tolist())
269
+ new_array = self.__class__(dtype, self.tolist())
242
270
  return new_array
243
271
 
244
272
  def tolist(self) -> List[ElementType]:
245
- return [self._dtype.read_fn(self.data, start=start)
273
+ return [self._dtype.read_fn(self.data, start=start)
246
274
  for start in range(0, len(self.data) - self._dtype.length + 1, self._dtype.length)]
247
275
 
248
276
  def append(self, x: ElementType) -> None:
@@ -250,13 +278,13 @@ class Array:
250
278
  raise ValueError("Cannot append to Array as its length is not a multiple of the format length.")
251
279
  self.data += self._create_element(x)
252
280
 
253
- def extend(self, iterable: Union[Array, array.array, Iterable]) -> None:
281
+ def extend(self, iterable: Union[Array, array.array, Iterable[Any]]) -> None:
254
282
  if len(self.data) % self._dtype.length != 0:
255
283
  raise ValueError(f"Cannot extend Array as its data length ({len(self.data)} bits) is not a multiple of the format length ({self._dtype.length} bits).")
256
284
  if isinstance(iterable, Array):
257
285
  if self._dtype.name != iterable._dtype.name or self._dtype.length != iterable._dtype.length:
258
286
  raise TypeError(
259
- f"Cannot extend an Array with format '{self._fmt}' from an Array of format '{iterable._fmt}'.")
287
+ f"Cannot extend an Array with format '{self._dtype}' from an Array of format '{iterable._dtype}'.")
260
288
  # No need to iterate over the elements, we can just append the data
261
289
  self.data.append(iterable.data)
262
290
  elif isinstance(iterable, array.array):
@@ -264,10 +292,10 @@ class Array:
264
292
  name_value = utils.parse_single_struct_token('=' + iterable.typecode)
265
293
  if name_value is None:
266
294
  raise ValueError(f"Cannot extend from array with typecode {iterable.typecode}.")
267
- other_dtype = dtype_register.get_dtype(*name_value)
295
+ other_dtype = dtype_register.get_dtype(*name_value, scale=None)
268
296
  if self._dtype.name != other_dtype.name or self._dtype.length != other_dtype.length:
269
297
  raise ValueError(
270
- f"Cannot extend an Array with format '{self._fmt}' from an array with typecode '{iterable.typecode}'.")
298
+ f"Cannot extend an Array with format '{self._dtype}' from an array with typecode '{iterable.typecode}'.")
271
299
  self.data += iterable.tobytes()
272
300
  else:
273
301
  if isinstance(iterable, str):
@@ -302,7 +330,7 @@ class Array:
302
330
  """
303
331
  if self._dtype.length % 8 != 0:
304
332
  raise ValueError(
305
- f"byteswap can only be used for whole-byte elements. The '{self._fmt}' format is {self._dtype.length} bits long.")
333
+ f"byteswap can only be used for whole-byte elements. The '{self._dtype}' format is {self._dtype.length} bits long.")
306
334
  self.data.byteswap(self.itemsize // 8)
307
335
 
308
336
  def count(self, value: ElementType) -> int:
@@ -368,7 +396,7 @@ class Array:
368
396
  stream -- A TextIO object with a write() method. Defaults to sys.stdout.
369
397
 
370
398
  """
371
- colour = Colour(options.colourful_prettyprinting)
399
+ colour = Colour(not options.no_color)
372
400
  sep = ' '
373
401
  dtype2 = None
374
402
  tidy_fmt = None
@@ -381,7 +409,6 @@ class Array:
381
409
  if len(token_list) not in [1, 2]:
382
410
  raise ValueError(f"Only one or two tokens can be used in an Array.pp() format - '{fmt}' has {len(token_list)} tokens.")
383
411
  dtype1 = Dtype(*utils.parse_name_length_token(token_list[0]))
384
- parameter_str = f"fmt='{fmt}'"
385
412
  if len(token_list) == 2:
386
413
  dtype2 = Dtype(*utils.parse_name_length_token(token_list[1]))
387
414
 
@@ -405,7 +432,7 @@ class Array:
405
432
  data = self.data if trailing_bit_length == 0 else self.data[0: -trailing_bit_length]
406
433
  length = len(self.data) // token_length
407
434
  len_str = colour.green + str(length) + colour.off
408
- stream.write(f"<Array {tidy_fmt}, length={len_str}, itemsize={token_length} bits, total data size={(len(self.data) + 7) // 8} bytes> [\n")
435
+ stream.write(f"<{self.__class__.__name__} {tidy_fmt}, length={len_str}, itemsize={token_length} bits, total data size={(len(self.data) + 7) // 8} bytes> [\n")
409
436
  data._pp(dtype1, dtype2, token_length, width, sep, format_sep, show_offset, stream, False, token_length)
410
437
  stream.write("]")
411
438
  if trailing_bit_length != 0:
@@ -443,13 +470,13 @@ class Array:
443
470
  start += self._dtype.length
444
471
 
445
472
  def __copy__(self) -> Array:
446
- a_copy = Array(self._fmt)
473
+ a_copy = self.__class__(self._dtype)
447
474
  a_copy.data = copy.copy(self.data)
448
475
  return a_copy
449
476
 
450
477
  def _apply_op_to_all_elements(self, op, value: Union[int, float, None], is_comparison: bool = False) -> Array:
451
478
  """Apply op with value to each element of the Array and return a new Array"""
452
- new_array = Array('bool' if is_comparison else self._dtype)
479
+ new_array = self.__class__('bool' if is_comparison else self._dtype)
453
480
  new_data = BitArray()
454
481
  failures = index = 0
455
482
  msg = ''
@@ -505,7 +532,7 @@ class Array:
505
532
  """Apply op with value to each element of the Array as an unsigned integer in place."""
506
533
  value = BitArray._create_from_bitstype(value)
507
534
  if len(value) != self._dtype.length:
508
- raise ValueError(f"Bitwise op needs a bitstring of length {self._dtype.length} to match format {self._fmt}.")
535
+ raise ValueError(f"Bitwise op needs a bitstring of length {self._dtype.length} to match format {self._dtype}.")
509
536
  for start in range(0, len(self) * self._dtype.length, self._dtype.length):
510
537
  self.data[start: start + self._dtype.length] = op(self.data[start: start + self._dtype.length], value)
511
538
  return self
@@ -520,7 +547,7 @@ class Array:
520
547
  new_type = dtype_register.get_dtype('bool', 1)
521
548
  else:
522
549
  new_type = self._promotetype(self._dtype, other._dtype)
523
- new_array = Array(new_type)
550
+ new_array = self.__class__(new_type)
524
551
  new_data = BitArray()
525
552
  failures = index = 0
526
553
  msg = ''
@@ -553,7 +580,7 @@ class Array:
553
580
 
554
581
  """
555
582
  def is_float(x): return x.return_type is float
556
- def is_int(x): return x.return_type is int
583
+ def is_int(x): return x.return_type is int or x.return_type is bool
557
584
  if is_float(type1) + is_int(type1) + is_float(type2) + is_int(type2) != 2:
558
585
  raise ValueError(f"Only integer and floating point types can be combined - not '{type1}' and '{type2}'.")
559
586
  # If same type choose the widest
@@ -722,15 +749,21 @@ class Array:
722
749
  return self._apply_op_between_arrays(operator.le, other, is_comparison=True)
723
750
  return self._apply_op_to_all_elements(operator.le, other, is_comparison=True)
724
751
 
725
- def __eq__(self, other: Union[int, float, str, BitsType, Array]) -> Array:
726
- if isinstance(other, Array):
727
- return self._apply_op_between_arrays(operator.eq, other, is_comparison=True)
728
- return self._apply_op_to_all_elements(operator.eq, other, is_comparison=True)
752
+ def _eq_ne(self, op, other: Any) -> Array:
753
+ if isinstance(other, (int, float, str, Bits)):
754
+ return self._apply_op_to_all_elements(op, other, is_comparison=True)
755
+ try:
756
+ other = self.__class__(self.dtype, other)
757
+ except:
758
+ return NotImplemented
759
+ finally:
760
+ return self._apply_op_between_arrays(op, other, is_comparison=True)
729
761
 
730
- def __ne__(self, other: Union[int, float, str, BitsType, Array]) -> Array:
731
- if isinstance(other, Array):
732
- return self._apply_op_between_arrays(operator.ne, other, is_comparison=True)
733
- return self._apply_op_to_all_elements(operator.ne, other, is_comparison=True)
762
+ def __eq__(self, other: Any) -> Array:
763
+ return self._eq_ne(operator.eq, other)
764
+
765
+ def __ne__(self, other: Any) -> Array:
766
+ return self._eq_ne(operator.ne, other)
734
767
 
735
768
  # Unary operators
736
769