Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add naturallist #110

Open
wants to merge 12 commits into
base: main
Choose a base branch
from
8 changes: 2 additions & 6 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -152,12 +152,8 @@ dmypy.json
# Cython debug symbols
cython_debug/

# PyCharm
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
# and can be added to the global gitignore or merged into this file. For a more nuclear
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
#.idea/
# Jetbrains IDEs
.idea/

# hatch-vcs
src/*/_version.py
1 change: 1 addition & 0 deletions docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ Welcome to the humanize API reference.
- [Time](time.md)
- [Filesize](filesize.md)
- [I18n](i18n.md)
- [Lists](lists.md)

{%
include-markdown "../README.md"
Expand Down
3 changes: 3 additions & 0 deletions docs/lists.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Lists

::: humanize.lists
1 change: 1 addition & 0 deletions mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ nav:
- Time: time.md
- Filesize: filesize.md
- Internationalisation: i18n.md
- Lists: lists.md

plugins:
- search
Expand Down
2 changes: 2 additions & 0 deletions src/humanize/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

from humanize.filesize import naturalsize
from humanize.i18n import activate, deactivate, decimal_separator, thousands_separator
from humanize.lists import naturallist
from humanize.number import (
apnumber,
clamp,
Expand Down Expand Up @@ -38,6 +39,7 @@
"naturaldate",
"naturalday",
"naturaldelta",
"naturallist",
"naturalsize",
"naturaltime",
"ordinal",
Expand Down
35 changes: 35 additions & 0 deletions src/humanize/lists.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
"""Lists related humanization."""

from __future__ import annotations

from collections.abc import Iterable
from typing import Any

__all__ = ["naturallist"]


def naturallist(items: Iterable[Any]) -> str:
"""Natural list.

Convert an iterable of items into a human-readable string with commas and 'and'.

Examples:
>>> naturallist(["one", "two", "three"])
'one, two and three'
>>> naturallist(["one", "two"])
'one and two'
>>> naturallist(["one"])
'one'

Args:
items (Iterable): An iterable of items.

Returns:
str: A string with commas and 'and' in the right places.
"""
if len(items) == 1:
return str(items[0])
elif len(items) == 2:
return f"{str(items[0])} and {str(items[1])}"
else:
return ", ".join(str(item) for item in items[:-1]) + f" and {str(items[-1])}"
23 changes: 23 additions & 0 deletions tests/test_lists.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
from __future__ import annotations

import pytest

import humanize


@pytest.mark.parametrize(
"test_args, expected",
[
([["1", "2", "3"]], "1, 2 and 3"),
([["one", "two", "three"]], "one, two and three"),
([["one", "two"]], "one and two"),
([["one"]], "one"),
([[""]], ""),
([[1, 2, 3]], "1, 2 and 3"),
([[1, "two"]], "1 and two"),
],
)
def test_naturallist(
test_args: list[str] | list[int] | list[str | int], expected: str
) -> None:
assert humanize.naturallist(*test_args) == expected
Loading