Skip to content

Commit f94eb5b

Browse files
committed
Blocks is (start, data) tuple instead of ambiguous list
Signed-off-by: Andrea Zoppi <[email protected]>
1 parent ccc840e commit f94eb5b

File tree

9 files changed

+1476
-1380
lines changed

9 files changed

+1476
-1380
lines changed

CHANGELOG.rst

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,15 @@
11
Changelog
22
=========
33

4+
1.1.0 (2025-07-04)
5+
------------------
6+
7+
* Describing blocks with ``(start, data)`` tuples, instead of ambiguous lists.
8+
* Removed support for Python 3.7.
9+
* Removed support for Python 3.8.
10+
* Improved type support.
11+
12+
413
1.0.2 (2025-06-09)
514
------------------
615

README.rst

Lines changed: 26 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,7 @@ Here's a quick usage example of ``bytesparse`` objects:
128128
>>> len(m) # total length
129129
13
130130
>>> str(m) # string representation, with bounds and data blocks
131-
"<[[0, b'Hello, World!']]>"
131+
"<[(0, b'Hello, World!')]>"
132132
>>> bytes(m) # exports as bytes
133133
b'Hello, World!'
134134
>>> m.to_bytes() # exports the whole range as bytes
@@ -174,7 +174,7 @@ b'Ciao, Word!'
174174
>>> i = m.index(b',') # gets the address of the comma
175175
>>> m.clear(start=i, endex=(i + 2)) # makes empty space between the words
176176
>>> m.to_blocks() # exports as data block list
177-
[[0, b'Ciao'], [6, b'Word!']]
177+
[(0, b'Ciao'), (6, b'Word!')]
178178
>>> m.contiguous # multiple data blocks (emptiness inbetween)
179179
False
180180
>>> m.content_parts # two data blocks
@@ -198,53 +198,53 @@ b'Ciao..Work!'
198198

199199
>>> m.crop(start=m.index(b'W')) # keeps 'Work!'
200200
>>> m.to_blocks()
201-
[[6, b'Work!']]
201+
[(6, b'Work!')]
202202
>>> m.span # address range of the whole memory
203203
(6, 11)
204204
>>> m.start, m.endex # same as above
205205
(6, 11)
206206

207207
>>> m.bound_span = (2, 10) # sets memory address bounds
208208
>>> str(m)
209-
"<2, [[6, b'Work']], 10>"
209+
"<2, [(6, b'Work')], 10>"
210210
>>> m.to_blocks()
211-
[[6, b'Work']]
211+
[(6, b'Work')]
212212

213213
>>> m.shift(-6) # shifts to the left; NOTE: address bounds will cut 2 bytes!
214214
>>> m.to_blocks()
215-
[[2, b'rk']]
215+
[(2, b'rk')]
216216
>>> str(m)
217-
"<2, [[2, b'rk']], 10>"
217+
"<2, [(2, b'rk')], 10>"
218218

219219
>>> a = bytesparse(b'Ma')
220-
>>> a.write(0, m) # writes [2, b'rk'] --> 'Mark'
220+
>>> a.write(0, m) # writes (2, b'rk') --> 'Mark'
221221
>>> a.to_blocks()
222-
[[0, b'Mark']]
222+
[(0, b'Mark')]
223223

224224
>>> b = Memory.from_bytes(b'ing', offset=4)
225225
>>> b.to_blocks()
226-
[[4, b'ing']]
226+
[(4, b'ing')]
227227

228-
>>> a.write(0, b) # writes [4, b'ing'] --> 'Marking'
228+
>>> a.write(0, b) # writes (4, b'ing') --> 'Marking'
229229
>>> a.to_blocks()
230-
[[0, b'Marking']]
230+
[(0, b'Marking')]
231231

232232
>>> a.reserve(4, 2) # inserts 2 empty bytes after 'Mark'
233233
>>> a.to_blocks()
234-
[[0, b'Mark'], [6, b'ing']]
234+
[(0, b'Mark'), (6, b'ing')]
235235

236236
>>> a.write(4, b'et') # --> 'Marketing'
237237
>>> a.to_blocks()
238-
[[0, b'Marketing']]
238+
[(0, b'Marketing')]
239239

240240
>>> a.fill(1, -1, b'*') # fills asterisks between the first and last letters
241241
>>> a.to_blocks()
242-
[[0, b'M*******g']]
242+
[(0, b'M*******g')]
243243

244244
>>> v = a.view(1, -1) # creates a memory view spanning the asterisks
245245
>>> v[::2] = b'1234' # replaces even asterisks with numbers
246246
>>> a.to_blocks()
247-
[[0, b'M1*2*3*4g']]
247+
[(0, b'M1*2*3*4g')]
248248
>>> a.count(b'*') # counts all the asterisks
249249
3
250250
>>> v.release() # release memory view
@@ -257,11 +257,11 @@ False
257257

258258
>>> del a[a.index(b'*')::2] # deletes every other byte from the first asterisk
259259
>>> a.to_blocks()
260-
[[0, b'M1234']]
260+
[(0, b'M1234')]
261261

262262
>>> a.shift(3) # moves away from the trivial 0 index
263263
>>> a.to_blocks()
264-
[[3, b'M1234']]
264+
[(3, b'M1234')]
265265
>>> list(a.keys())
266266
[3, 4, 5, 6, 7]
267267
>>> list(a.values())
@@ -270,30 +270,30 @@ False
270270
[(3, 77), (4, 49), (5, 50), (6, 51), (7, 52)]
271271

272272
>>> c.to_blocks() # reminder
273-
[[0, b'M1*2*3*4g']]
273+
[(0, b'M1*2*3*4g')]
274274
>>> c[2::2] = None # clears (empties) every other byte from the first asterisk
275275
>>> c.to_blocks()
276-
[[0, b'M1'], [3, b'2'], [5, b'3'], [7, b'4']]
276+
[(0, b'M1'), (3, b'2'), (5, b'3'), (7, b'4')]
277277
>>> list(c.intervals()) # lists all the block ranges
278278
[(0, 2), (3, 4), (5, 6), (7, 8)]
279279
>>> list(c.gaps()) # lists all the empty ranges
280280
[(None, 0), (2, 3), (4, 5), (6, 7), (8, None)]
281281

282282
>>> c.flood(pattern=b'xy') # fills empty spaces
283283
>>> c.to_blocks()
284-
[[0, b'M1x2x3x4']]
284+
[(0, b'M1x2x3x4')]
285285

286286
>>> t = c.cut(c.index(b'1'), c.index(b'3')) # cuts an inner slice
287287
>>> t.to_blocks()
288-
[[1, b'1x2x']]
288+
[(1, b'1x2x')]
289289
>>> c.to_blocks()
290-
[[0, b'M'], [5, b'3x4']]
290+
[(0, b'M'), (5, b'3x4')]
291291
>>> t.bound_span # address bounds of the slice (automatically activated)
292292
(1, 5)
293293

294-
>>> k = bytesparse.from_blocks([[4, b'ABC'], [9, b'xy']], start=2, endex=15) # bounded
294+
>>> k = bytesparse.from_blocks([(4, b'ABC'), (9, b'xy')], start=2, endex=15) # bounded
295295
>>> str(k) # shows summary
296-
"<2, [[4, b'ABC'], [9, b'xy']], 15>"
296+
"<2, [(4, b'ABC'), (9, b'xy')], 15>"
297297
>>> k.bound_span # defined at creation
298298
(2, 15)
299299
>>> k.span # superseded by bounds
@@ -307,7 +307,7 @@ False
307307

308308
>>> k.flood(pattern=b'.') # floods between span
309309
>>> k.to_blocks()
310-
[[2, b'..ABC..xy....']]
310+
[(2, b'..ABC..xy....')]
311311

312312

313313
Background

src/bytesparse/__init__.py

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -53,8 +53,8 @@
5353
| | | | | |[x | y | z]| |
5454
+---+---+---+---+---+---+---+---+---+
5555
56-
>>> a = [1, b'ABC']
57-
>>> b = [5, b'xyz']
56+
>>> a = (1, b'ABC')
57+
>>> b = (5, b'xyz')
5858
5959
Instead, *overlapping* blocks have at least an addressed cell occupied by
6060
more items:
@@ -71,10 +71,10 @@
7171
| | |[!]| | | | | | |
7272
+---+---+---+---+---+---+---+---+---+
7373
74-
>>> a = [1, b'ABC']
75-
>>> b = [3, b'xyz']
76-
>>> c = [0, b'##']
77-
>>> d = [2, b'!']
74+
>>> a = (1, b'ABC')
75+
>>> b = (3, b'xyz')
76+
>>> c = (0, b'##')
77+
>>> d = (2, b'!')
7878
7979
Contiguous blocks are *non-overlapping*.
8080
@@ -86,33 +86,33 @@
8686
| | | | |[x | y | z]| | |
8787
+---+---+---+---+---+---+---+---+---+
8888
89-
>>> a = [1, b'ABC']
90-
>>> b = [4, b'xyz']
89+
>>> a = (1, b'ABC')
90+
>>> b = (4, b'xyz')
9191
9292
This module often deals with *sequences* of blocks, typically :obj:`list`
9393
objects containing blocks:
9494
95-
>>> seq = [[1, b'ABC'], [5, b'xyz']]
95+
>>> seq = [(1, b'ABC'), (5, b'xyz')]
9696
9797
Sometimes *sequence generators* are allowed, in that blocks of the sequence
9898
are yielded on-the-fly by a generator, like `seq_gen`:
9999
100100
>>> seq_gen = ([i, (i + 0x21).to_bytes(1, 'little') * 3] for i in range(0, 15, 5))
101101
>>> list(seq_gen)
102-
[[0, b'!!!'], [5, b'&&&'], [10, b'+++']]
102+
[(0, b'!!!'), (5, b'&&&'), (10, b'+++')]
103103
104104
It is required that sequences are ordered, which means that a block ``b`` must
105105
follow a block ``a`` which end address is lesser than the `start` of ``b``,
106106
like in:
107107
108-
>>> a = [1, b'ABC']
109-
>>> b = [5, b'xyz']
108+
>>> a = (1, b'ABC')
109+
>>> b = (5, b'xyz')
110110
>>> a[0] + len(a[1]) <= b[0]
111111
True
112112
113113
"""
114114

115-
__version__ = '1.0.2'
115+
__version__ = '1.1.0'
116116

117117
from .inplace import Memory
118118
from .inplace import bytesparse

0 commit comments

Comments
 (0)