series_phrase_serial()
Join a sequence into a human-readable series **with** an Oxford comma for 3+ items, even if the user supplied a custom `last_sep` without the comma.
Implementation
Args: items: Iterable of values (None→empty list) sep: Separator between items (e.g. ", " or ";") last_sep: Separator before last item (e.g. ", and " or " or ") Returns: A string like "a, b, c, and d" or with custom separators.
Example
series_phrase_serial(["foo","bar","baz","qux"],", "," or ")
Expected output: "foo, bar, baz, or qux"
Source Code
def series_phrase_serial(
items: Any,
sep: Any = ", ",
last_sep: Any = ", and "
) -> str:
seq = [str(x) for x in (items or []) if x is not None and str(x) != ""]
n = len(seq)
if n == 0:
return ""
if n == 1:
return seq[0]
if n == 2:
# for two items always use plain " and "
return seq[0] + " and " + seq[1]
# n >= 3: enforce Oxford comma
sep_str = "" if sep is None else str(sep)
last_str = "" if last_sep is None else str(last_sep)
# if user‐supplied last_sep doesn’t already start with the sep’s punctuation,
# prefix it so we get the comma (or semicolon, etc.) back.
lead = sep_str.rstrip()
if last_str.strip() and not last_str.lstrip().startswith(lead):
last_str = lead + last_str
return sep_str.join(seq[:-1]) + last_str + seq[-1]