Skip to content

Commit

Permalink
Merge pull request #2166 from sopel-irc/announce-py3-dict.keys
Browse files Browse the repository at this point in the history
announce: make `_chunks()` helper safe against more input types

Cherry-picked from master: b38a5d6
  • Loading branch information
dgw committed Aug 10, 2021
1 parent 6c29559 commit ae5ba24
Show file tree
Hide file tree
Showing 2 changed files with 45 additions and 3 deletions.
16 changes: 13 additions & 3 deletions sopel/modules/announce.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
"""
from __future__ import absolute_import, division, print_function, unicode_literals

import itertools

from sopel import plugin


Expand All @@ -18,10 +20,18 @@ def _chunks(items, size):
:param items: the collection of items to chunk
:type items: :term:`iterable`
:param int size: the size of each chunk
:return: a :term:`generator` of chunks
:rtype: :term:`generator` of :class:`tuple`
"""
# from https://stackoverflow.com/a/312464/5991 with modified names for readability
for delim in range(0, len(items), size):
yield items[delim:delim + size]
# This approach is safer than slicing with non-subscriptable types,
# for example `dict_keys` objects
iterator = iter(items)
# TODO: Simplify to assignment expression (`while cond := expr`)
# when dropping Python 3.7
chunk = tuple(itertools.islice(iterator, size))
while chunk:
yield chunk
chunk = tuple(itertools.islice(iterator, size))


@plugin.command('announce')
Expand Down
32 changes: 32 additions & 0 deletions test/modules/test_modules_announce.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
"""Tests for Sopel's ``announce`` plugin"""
from __future__ import generator_stop

from sopel.modules import announce


def test_chunks():
"""Test the `_chunks` helper for functionality and compatibility."""
# list input
items = ['list', 'of', 'items', 'to', 'chunk']
r = list(announce._chunks(items, 2))

assert len(r) == 3
assert r[0] == tuple(items[:2])
assert r[1] == tuple(items[2:4])
assert r[2] == tuple(items[4:])

# tuple input
items = ('tuple', 'of', 'items')
r = list(announce._chunks(items, 3))

assert len(r) == 1
assert r[0] == items

# dict keys input
keys = {'one': True, 'two': True, 'three': True}.keys()
items = list(keys)
r = list(announce._chunks(keys, 1))

assert len(r) == 3
for idx in range(len(items)):
assert r[idx] == (items[idx],)

0 comments on commit ae5ba24

Please sign in to comment.