Skip to content

Commit 45e873d

Browse files
committed
tools: add chunk method
Chunking data is something we do frequently, so it makes sense to have a helper function to do this.
1 parent 33d05ce commit 45e873d

File tree

6 files changed

+67
-30
lines changed

6 files changed

+67
-30
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
9090
## [1.0.0-alpha.4] - 2021-04-12
9191
### Added
9292
- Size check when restoring firmware via USB/DFU.
93+
- Added `pybricksdev.tools.chunk()` function.
9394
### Fixed
9495
- Wait for some time to allow program output to be received before disconnecting
9596
in the `run` command.

pybricksdev/ble/__init__.py

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
from bleak.backends.device import BLEDevice
1010
from bleak.backends.scanner import AdvertisementData
1111

12+
from ..tools import chunk
1213
from .pybricks import PYBRICKS_SERVICE_UUID
1314

1415
logger = logging.getLogger(__name__)
@@ -131,16 +132,14 @@ async def write(self, data, with_response=False):
131132
Write with or without response.
132133
"""
133134
# Send the chunks one by one
134-
for i in range(0, len(data), self.max_data_size):
135-
chunk = data[i : i + self.max_data_size]
135+
for c in chunk(data, self.max_data_size):
136136
logger.debug(
137137
"TX CHUNK: {0}, {1} response".format(
138-
chunk, "with" if with_response else "without"
138+
c, "with" if with_response else "without"
139139
)
140140
)
141-
# Send one chunk
142141
await self.client.write_gatt_char(
143-
self.char_rx_UUID, bytearray(chunk), with_response
142+
self.char_rx_UUID, bytearray(c), with_response
144143
)
145144

146145

pybricksdev/compile.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99

1010
import mpy_cross
1111

12+
from .tools import chunk
13+
1214
logger = logging.getLogger(__name__)
1315

1416
BUILD_DIR = "build"
@@ -138,9 +140,8 @@ def print_mpy(data):
138140
print(f"// MPY file. Version: {data[1]}. Size: {len(data)} bytes")
139141
print("const uint8_t script[] = {")
140142

141-
for i in range(0, len(data), WIDTH):
142-
chunk = data[i : i + WIDTH]
143-
hex_repr = [f"0x{i:02X}" for i in chunk]
143+
for c in chunk(data, WIDTH):
144+
hex_repr = [f"0x{i:02X}" for i in c]
144145
print(f" {', '.join(hex_repr)},")
145146

146147
print("};")

pybricksdev/connections.py

Lines changed: 10 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
unpack_pnp_id,
3030
)
3131
from .compile import compile_file
32+
from .tools import chunk
3233
from .tools.checksum import xor_bytes
3334
from .usbconnection import USBConnection
3435

@@ -295,17 +296,13 @@ async def run(self, py_path, wait=True, print_output=True):
295296
length = len(mpy).to_bytes(4, byteorder="little")
296297
await self.send_message(length)
297298

298-
# Divide script in chunks of bytes
299-
n = 100
300-
chunks = [mpy[i : i + n] for i in range(0, len(mpy), n)]
301-
302299
# Send the data chunk by chunk
303300
with logging_redirect_tqdm(), tqdm(
304301
total=len(mpy), unit="B", unit_scale=True
305302
) as pbar:
306-
for chunk in chunks:
307-
await self.send_message(chunk)
308-
pbar.update(len(chunk))
303+
for c in chunk(mpy, 100):
304+
await self.send_message(c)
305+
pbar.update(len(c))
309306

310307
# Optionally wait for the program to finish
311308
if wait:
@@ -424,11 +421,6 @@ async def run(self, py_path, wait=False):
424421
with open(py_path, "rb") as demo:
425422
program = demo.read()
426423

427-
chunk_size = 512
428-
chunks = [
429-
program[i : i + chunk_size] for i in range(0, len(program), chunk_size)
430-
]
431-
432424
while response is None or "transferid" not in response:
433425
response = await self.send_command_and_get_response(
434426
"start_write_program",
@@ -450,15 +442,15 @@ async def run(self, py_path, wait=False):
450442
with logging_redirect_tqdm(), tqdm(
451443
total=len(program), unit="B", unit_scale=True
452444
) as pbar:
453-
for chunk in chunks:
445+
for c in chunk(program, 512):
454446
response = await self.send_command_and_get_response(
455447
"write_package",
456448
{
457-
"data": base64.b64encode(chunk).decode("ascii"),
449+
"data": base64.b64encode(c).decode("ascii"),
458450
"transferid": transferid,
459451
},
460452
)
461-
pbar.update(len(chunk))
453+
pbar.update(len(c))
462454

463455
await asyncio.sleep(0.5)
464456
response = await self.send_command_and_get_response(
@@ -798,17 +790,13 @@ async def run(self, py_path, wait=True, print_output=True):
798790
length = len(mpy).to_bytes(4, byteorder="little")
799791
await self.send_block(length)
800792

801-
# Divide script in chunks of bytes
802-
n = 100
803-
chunks = [mpy[i : i + n] for i in range(0, len(mpy), n)]
804-
805793
# Send the data chunk by chunk
806794
with logging_redirect_tqdm(), tqdm(
807795
total=len(mpy), unit="B", unit_scale=True
808796
) as pbar:
809-
for chunk in chunks:
810-
await self.send_block(chunk)
811-
pbar.update(len(chunk))
797+
for c in chunk(mpy, 100):
798+
await self.send_block(c)
799+
pbar.update(len(c))
812800
finally:
813801
self.loading = False
814802

pybricksdev/tools/__init__.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
# SPDX-License-Identifier: MIT
2+
# Copyright (c) 2021 The Pybricks Authors
3+
4+
from typing import Generator, Sequence, TypeVar
5+
6+
T = TypeVar("T")
7+
8+
9+
def chunk(data: Sequence[T], size: int) -> Generator[Sequence[T], None, None]:
10+
"""
11+
Divides a sequence into equal-sized chunks.
12+
13+
The last chunk may be smaller than *size*.
14+
15+
Args:
16+
data: The data to be divided.
17+
size: The size of each chunk.
18+
19+
Returns:
20+
A generator that yields each chunk.
21+
"""
22+
for i in range(0, len(data), size):
23+
yield data[i : i + size]
24+
25+
26+
__all__ = ["chunk"]

tests/test_chunk.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
# SPDX-License-Identifier: MIT
2+
# Copyright (c) 2021 The Pybricks Authors
3+
4+
5+
from pybricksdev.tools import chunk
6+
7+
8+
def test_chunk():
9+
expected = [
10+
[1, 2, 3],
11+
[4, 5, 6],
12+
[7, 8, 9],
13+
[10],
14+
]
15+
16+
count = 0
17+
18+
for c, e in zip(chunk([1, 2, 3, 4, 5, 6, 7, 8, 9, 10], 3), expected):
19+
assert c == e
20+
count += 1
21+
22+
assert count == len(expected)

0 commit comments

Comments
 (0)