Files
dgilland-pydash/PR.md
T
dualfroz 7fc28b38e1 fix(strings): keep last char when truncate separator is absent
str.rfind returns -1 when a string separator is not present in the
truncated text. Using that -1 as a slice end dropped an extra trailing
character before appending the omission. Only adjust the truncation point
when the separator is actually found, matching the regex-separator branch
and lodash behaviour.
2026-09-04 09:04:55 +02:00

2.7 KiB

Fix truncate dropping a character when a string separator is absent

Problem

pydash.truncate(text, length, omission, separator) is documented to truncate text at the last occurrence of separator. When separator is a string that does not occur in the truncated text, truncate incorrectly removes an extra trailing character instead of falling back to a plain cut.

>>> import pydash
>>> pydash.truncate("hello world", 10, "...", "xyz")
'hello ...'      # expected 'hello w...'
>>> pydash.truncate("hello world", 8, "...", "q")
'hell...'        # expected 'hello...'

This is inconsistent with the equivalent lodash behaviour (the API pydash mirrors), where a separator that is not found leaves the plain truncation untouched:

_.truncate('hello world', {length: 10, separator: 'xyz'}) // 'hello w...'
_.truncate('hello world', {length: 8,  separator: 'q'})   // 'hello...'

It is also internally inconsistent: the regular-expression separator branch already handles "no match" correctly (it leaves trunc_len at the full truncated length), while the string branch does not.

Root cause

src/pydash/strings.py, in truncate:

if pyd.is_string(separator):
    trunc_len = text.rfind(separator)

str.rfind returns -1 when the separator is not present. That -1 is then used as a slice end (text[:trunc_len] == text[:-1]), which silently drops the last character of the already-truncated text before appending omission.

Fix

Only move the truncation point when the separator is actually found:

if pyd.is_string(separator):
    separator_index = text.rfind(separator)
    if separator_index >= 0:
        trunc_len = separator_index

This mirrors the existing regex branch, which only updates trunc_len when a match exists.

Test

Added two cases to the existing test_truncate parametrization in tests/test_strings.py covering a string separator that does not occur in the truncated text:

(("hello world", 10, "...", "xyz"), "hello w..."),
(("hello world", 8, "...", "q"), "hello..."),

Gate

  • python -m pytest tests/test_strings.py -k test_truncate -> 13 passed
  • python -m pytest tests/ --ignore=tests/pytest_mypy_testing -> 1917 passed

Counterfactual (fix reverted, test kept): the new cases fail with AssertionError: assert 'hello ...' == 'hello w...' and AssertionError: assert 'hell...' == 'hello...'.

Flags for the account owner

The tests/pytest_mypy_testing/ suite fails locally with NameError (the reveal_type typing plugin is not resolvable in a bare pip install -e . environment). These failures are pre-existing and unrelated to this change, which touches only truncate.