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 natural_list #110

Merged
merged 14 commits into from
Oct 5, 2024
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 natural_list
from humanize.number import (
apnumber,
clamp,
Expand Down Expand Up @@ -38,6 +39,7 @@
"naturaldate",
"naturalday",
"naturaldelta",
"natural_list",
"naturalsize",
"naturaltime",
"ordinal",
Expand Down
34 changes: 34 additions & 0 deletions src/humanize/lists.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
"""Lists related humanization."""

from __future__ import annotations

from typing import Any

__all__ = ["natural_list"]


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

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

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

Args:
items (list): 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_natural_list(
test_args: list[str] | list[int] | list[str | int], expected: str
) -> None:
assert humanize.natural_list(*test_args) == expected
Loading