From 5821f19e57e94b4767418f5659a2e57cc25fae9e Mon Sep 17 00:00:00 2001 From: Arthur Clark Francisco Date: Tue, 25 Aug 2026 12:24:19 -0300 Subject: [PATCH 1/2] fix(lists): support general iterables and update typing in natural_list --- src/humanize/lists.py | 20 +++++++++++--------- tests/test_lists.py | 11 ++++++++++- 2 files changed, 21 insertions(+), 10 deletions(-) diff --git a/src/humanize/lists.py b/src/humanize/lists.py index 525f0e33..36e8ab73 100644 --- a/src/humanize/lists.py +++ b/src/humanize/lists.py @@ -4,15 +4,16 @@ TYPE_CHECKING = False if TYPE_CHECKING: + from collections.abc import Iterable from typing import Any __all__ = ["natural_list"] -def natural_list(items: list[Any]) -> str: +def natural_list(items: Iterable[Any]) -> str: """Natural list. - Convert a list of items into a human-readable string with commas and 'and'. + Convert an iterable of items into a human-readable string with commas and 'and'. Examples: >>> natural_list(["one", "two", "three"]) @@ -23,16 +24,17 @@ def natural_list(items: list[Any]) -> str: 'one' Args: - items (list): An iterable of items. + items (Iterable): An iterable of items. Returns: str: A string with commas and 'and' in the right places. """ - if not items: + item_list = [str(item) for item in items] + if not item_list: return "" - if len(items) == 1: - return str(items[0]) - elif len(items) == 2: - return f"{str(items[0])} and {str(items[1])}" + if len(item_list) == 1: + return item_list[0] + elif len(item_list) == 2: + return f"{item_list[0]} and {item_list[1]}" else: - return ", ".join([str(item) for item in items[:-1]]) + f" and {str(items[-1])}" + return ", ".join(item_list[:-1]) + f" and {item_list[-1]}" diff --git a/tests/test_lists.py b/tests/test_lists.py index cc514f32..d058ec0b 100644 --- a/tests/test_lists.py +++ b/tests/test_lists.py @@ -1,5 +1,8 @@ from __future__ import annotations +from collections.abc import Iterable +from typing import Any + import pytest import humanize @@ -16,9 +19,15 @@ ([[""]], ""), ([[1, 2, 3]], "1, 2 and 3"), ([[1, "two"]], "1 and two"), + ([("one", "two", "three")], "one, two and three"), + ([("one", "two")], "one and two"), + ([("one",)], "one"), + ([{"one": 1, "two": 2}.keys()], "one and two"), + ([(x for x in ["one", "two", "three"])], "one, two and three"), + ([range(1, 4)], "1, 2 and 3"), ], ) def test_natural_list( - test_args: list[str] | list[int] | list[str | int], expected: str + test_args: Iterable[Any], expected: str ) -> None: assert humanize.natural_list(*test_args) == expected From 3de92efc3461bef09571e9a242671f2410f6a6a2 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 25 Aug 2026 15:30:06 +0000 Subject: [PATCH 2/2] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- tests/test_lists.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/tests/test_lists.py b/tests/test_lists.py index d058ec0b..9b3c1b77 100644 --- a/tests/test_lists.py +++ b/tests/test_lists.py @@ -27,7 +27,5 @@ ([range(1, 4)], "1, 2 and 3"), ], ) -def test_natural_list( - test_args: Iterable[Any], expected: str -) -> None: +def test_natural_list(test_args: Iterable[Any], expected: str) -> None: assert humanize.natural_list(*test_args) == expected