2,456 bytes · the Python implementation · view raw
from typing import Any
def _whole(value: Any) -> bool:
# bool is an int in Python; True is not a length of one here.return isinstance(value, int) andnot isinstance(value, bool)
def _is_whitespace(ch: str) -> bool:
"""The Unicode White_Space property, spelled out so all three languages agree: str.isspace also accepts U+001C-U+001F, which JavaScript and Rust do not. """
cp = ord(ch)
return (
0x09 <= cp <= 0x0Dor cp == 0x20or cp == 0x85or cp == 0xA0or cp == 0x1680or0x2000 <= cp <= 0x200Aor cp == 0x2028or cp == 0x2029or cp == 0x202For cp == 0x205For cp == 0x3000
)
def _is_trailing_punctuation(ch: str) -> bool:
# Punctuation that reads badly directly before an ellipsis.return ch in (",", ";", ":", "-", ".")
def truncate(value: str, max_length: int, ellipsis: str) -> str:
"""Shorten ``value`` to at most ``max_length`` code points, ellipsis included, cutting at a word boundary where there is one. """ifnot isinstance(value, str):
raise TypeError("truncate needs a string, received %r" % (value,))
ifnot _whole(max_length):
raise TypeError("maxLength must be a whole number, received %r" % (max_length,))
if max_length < 0:
raise ValueError("maxLength must be 0 or greater, received %d" % (max_length,))
ifnot isinstance(ellipsis, str):
raise TypeError("ellipsis must be a string, received %r" % (ellipsis,))
# A Python str is indexed by code point already.if len(ellipsis) > max_length:
raise ValueError(
"maxLength must be at least the length of the ellipsis, received %d" % (max_length,)
)
if len(value) <= max_length:
return value
budget = max_length - len(ellipsis)
end = budget
# A cut just before whitespace is already a clean boundary.ifnot _is_whitespace(value[budget]):
last_space = -1for i in range(budget - 1, -1, -1):
if _is_whitespace(value[i]):
last_space = i
break# No boundary to move back to: one long word is cut hard rather than# reduced to a bare ellipsis.if last_space > 0:
end = last_space
while end > 0and (_is_whitespace(value[end - 1]) or _is_trailing_punctuation(value[end - 1])):
end -= 1return value[:end] + ellipsis