Skip to content

Commit

Permalink
feat: truncate_lines
Browse files Browse the repository at this point in the history
  • Loading branch information
thorwhalen committed Jan 30, 2025
1 parent a019ca9 commit a51f26a
Show file tree
Hide file tree
Showing 2 changed files with 58 additions and 10 deletions.
6 changes: 4 additions & 2 deletions lkj/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,14 @@
indent_lines, # Indent all lines of a string
most_common_indent, # Get the most common indent of a multiline string
regex_based_substitution,
truncate_string_with_marker, # Truncate a string to a maximum length, inserting a marker in the middle.
truncate_string, # Truncate a string to a maximum length, inserting a marker in the middle.
truncate_lines, # Truncate a multiline string to a maximum number of lines
unique_affixes, # Get unique prefixes or suffixes of a list of strings
camel_to_snake, # Convert CamelCase to snake_case
snake_to_camel, # Convert snake_case to CamelCase
fields_of_string_format, # Extract field names from a string format
fields_of_string_formats, # Extract field names from an iterable of string formats
fields_of_string_formats, # Extract field names from an iterable of string formats,
truncate_string_with_marker, # Deprecated: Backcompatibility alias
)
from lkj.loggers import (
print_with_timestamp,
Expand Down
62 changes: 54 additions & 8 deletions lkj/strings.py
Original file line number Diff line number Diff line change
Expand Up @@ -136,9 +136,7 @@ def snake_to_camel(snake_string):


# Note: Vendored in i2.multi_objects and dol.util
def truncate_string_with_marker(
s, *, left_limit=15, right_limit=15, middle_marker='...'
):
def truncate_string(s: str, *, left_limit=15, right_limit=15, middle_marker='...'):
"""
Truncate a string to a maximum length, inserting a marker in the middle.
Expand All @@ -148,23 +146,23 @@ def truncate_string_with_marker(
If the string is shorter than the sum of the left_limit and right_limit,
the string is returned as is.
>>> truncate_string_with_marker('1234567890')
>>> truncate_string('1234567890')
'1234567890'
But if the string is longer than the sum of the limits, it is truncated:
>>> truncate_string_with_marker('1234567890', left_limit=3, right_limit=3)
>>> truncate_string('1234567890', left_limit=3, right_limit=3)
'123...890'
>>> truncate_string_with_marker('1234567890', left_limit=3, right_limit=0)
>>> truncate_string('1234567890', left_limit=3, right_limit=0)
'123...'
>>> truncate_string_with_marker('1234567890', left_limit=0, right_limit=3)
>>> truncate_string('1234567890', left_limit=0, right_limit=3)
'...890'
If you're using a specific parametrization of the function often, you can
create a partial function with the desired parameters:
>>> from functools import partial
>>> truncate_string = partial(truncate_string_with_marker, left_limit=2, right_limit=2, middle_marker='---')
>>> truncate_string = partial(truncate_string, left_limit=2, right_limit=2, middle_marker='---')
>>> truncate_string('1234567890')
'12---90'
>>> truncate_string('supercalifragilisticexpialidocious')
Expand All @@ -181,6 +179,54 @@ def truncate_string_with_marker(
return s[:left_limit] + middle_marker + s[-right_limit:]


truncate_string_with_marker = truncate_string # backwards compatibility alias


def truncate_lines(
s: str, top_limit: int = None, bottom_limit: int = None, middle_marker: str = '...'
) -> str:
"""
Truncates a string by limiting the number of lines from the top and bottom.
If the total number of lines is greater than top_limit + bottom_limit,
it keeps the first `top_limit` lines, keeps the last `bottom_limit` lines,
and replaces the omitted middle portion with a single line containing
`middle_marker`.
If top_limit or bottom_limit is None, it is treated as 0.
Example:
>>> text = '''Line1
... Line2
... Line3
... Line4
... Line5
... Line6'''
>>> print(truncate_lines(text, top_limit=2, bottom_limit=2))
Line1
Line2
...
Line5
Line6
"""
# Interpret None as zero for convenience
top = top_limit if top_limit is not None else 0
bottom = bottom_limit if bottom_limit is not None else 0

# Split on line boundaries (retaining any trailing newlines in each piece)
lines = s.splitlines(True)
total_lines = len(lines)

# If no need to truncate, return as is
if total_lines <= top + bottom:
return s

# Otherwise, keep the top lines, keep the bottom lines,
# and insert a single marker line in the middle
truncated = lines[:top] + [middle_marker + '\n'] + lines[-bottom:]
return ''.join(truncated)


# TODO: Generalize so that it can be used with regex keys (not escaped)
def regex_based_substitution(replacements: dict, regex=None, s: str = None):
"""
Expand Down

0 comments on commit a51f26a

Please sign in to comment.