lkj 0.1.43__tar.gz → 0.1.45__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {lkj-0.1.43 → lkj-0.1.45}/PKG-INFO +101 -1
- {lkj-0.1.43 → lkj-0.1.45}/README.md +100 -0
- {lkj-0.1.43 → lkj-0.1.45}/lkj/__init__.py +1 -0
- {lkj-0.1.43 → lkj-0.1.45}/lkj/strings.py +487 -1
- {lkj-0.1.43 → lkj-0.1.45}/lkj.egg-info/PKG-INFO +101 -1
- {lkj-0.1.43 → lkj-0.1.45}/setup.cfg +1 -1
- {lkj-0.1.43 → lkj-0.1.45}/LICENSE +0 -0
- {lkj-0.1.43 → lkj-0.1.45}/lkj/chunking.py +0 -0
- {lkj-0.1.43 → lkj-0.1.45}/lkj/dicts.py +0 -0
- {lkj-0.1.43 → lkj-0.1.45}/lkj/filesys.py +0 -0
- {lkj-0.1.43 → lkj-0.1.45}/lkj/funcs.py +0 -0
- {lkj-0.1.43 → lkj-0.1.45}/lkj/importing.py +0 -0
- {lkj-0.1.43 → lkj-0.1.45}/lkj/iterables.py +0 -0
- {lkj-0.1.43 → lkj-0.1.45}/lkj/loggers.py +0 -0
- {lkj-0.1.43 → lkj-0.1.45}/lkj/misc.py +0 -0
- {lkj-0.1.43 → lkj-0.1.45}/lkj.egg-info/SOURCES.txt +0 -0
- {lkj-0.1.43 → lkj-0.1.45}/lkj.egg-info/dependency_links.txt +0 -0
- {lkj-0.1.43 → lkj-0.1.45}/lkj.egg-info/not-zip-safe +0 -0
- {lkj-0.1.43 → lkj-0.1.45}/lkj.egg-info/top_level.txt +0 -0
- {lkj-0.1.43 → lkj-0.1.45}/setup.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: lkj
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.45
|
|
4
4
|
Summary: A dump of homeless useful utils
|
|
5
5
|
Home-page: https://github.com/thorwhalen/lkj
|
|
6
6
|
Author: Thor Whalen
|
|
@@ -167,3 +167,103 @@ import_object(dot_path: str)
|
|
|
167
167
|
>>> f is join
|
|
168
168
|
True
|
|
169
169
|
```
|
|
170
|
+
|
|
171
|
+
## Pretty Printing Lists
|
|
172
|
+
|
|
173
|
+
The `print_list` function provides flexible, human-friendly ways to display lists and collections. It supports multiple display styles and can be used in several ways.
|
|
174
|
+
|
|
175
|
+
### Basic Usage
|
|
176
|
+
|
|
177
|
+
```python
|
|
178
|
+
from lkj.strings import print_list
|
|
179
|
+
|
|
180
|
+
items = ['apple', 'banana', 'cherry', 'date', 'elderberry', 'fig']
|
|
181
|
+
|
|
182
|
+
# Different display styles
|
|
183
|
+
print_list(items, style='wrapped') # Automatic line wrapping
|
|
184
|
+
print_list(items, style='columns') # Column format
|
|
185
|
+
print_list(items, style='numbered') # Numbered list
|
|
186
|
+
print_list(items, style='bullet') # Bullet points
|
|
187
|
+
print_list(items, style='compact') # All on one line
|
|
188
|
+
print_list(items, style='table') # Table format
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
### Direct Usage with Customization
|
|
192
|
+
|
|
193
|
+
```python
|
|
194
|
+
# Customize width, separators, and formatting
|
|
195
|
+
print_list(items, style='wrapped', max_width=40, sep=' | ')
|
|
196
|
+
print_list(items, style='columns', items_per_line=3)
|
|
197
|
+
print_list(items, style='numbered', line_prefix=' ')
|
|
198
|
+
print_list(items, style='bullet', show_count=False)
|
|
199
|
+
|
|
200
|
+
# Return string instead of printing
|
|
201
|
+
result = print_list(items, style='numbered', print_func=None)
|
|
202
|
+
print(result)
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
### Partial Function Factory
|
|
206
|
+
|
|
207
|
+
When you don't specify the `items` parameter, `print_list` returns a partial function that you can reuse:
|
|
208
|
+
|
|
209
|
+
```python
|
|
210
|
+
# Create specialized printers
|
|
211
|
+
numbered_printer = print_list(style='numbered', show_count=False)
|
|
212
|
+
bullet_printer = print_list(style='bullet', print_func=None)
|
|
213
|
+
compact_printer = print_list(style='compact', max_width=60)
|
|
214
|
+
|
|
215
|
+
# Reuse with different data
|
|
216
|
+
numbered_printer(['a', 'b', 'c']) # Prints: 1. a\n2. b\n3. c
|
|
217
|
+
result = bullet_printer(['x', 'y', 'z']) # Returns: '• x\n• y\n• z'
|
|
218
|
+
compact_printer(['item1', 'item2']) # Prints: item1, item2
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
### Convenience Methods
|
|
222
|
+
|
|
223
|
+
The `print_list` object provides convenient pre-configured methods:
|
|
224
|
+
|
|
225
|
+
```python
|
|
226
|
+
# Quick access to common styles
|
|
227
|
+
print_list.compact(items) # Compact format, no count
|
|
228
|
+
print_list.wrapped(items) # Wrapped format, no count
|
|
229
|
+
print_list.columns(items) # Column format, no count
|
|
230
|
+
print_list.numbered(items) # Numbered format, no count
|
|
231
|
+
print_list.bullets(items) # Bullet format, no count
|
|
232
|
+
|
|
233
|
+
# Specialized methods
|
|
234
|
+
print_list.as_table(data) # Table with headers
|
|
235
|
+
print_list.summary(items) # Summary for long lists
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
### Advanced Examples
|
|
239
|
+
|
|
240
|
+
```python
|
|
241
|
+
# Table with custom data
|
|
242
|
+
data = [['Name', 'Age', 'City'], ['Alice', 25, 'NYC'], ['Bob', 30, 'LA']]
|
|
243
|
+
print_list.as_table(data)
|
|
244
|
+
|
|
245
|
+
# Summary for long lists
|
|
246
|
+
long_list = list(range(100))
|
|
247
|
+
print_list.summary(long_list, max_items=6) # Shows: [0, 1, 2, ..., 97, 98, 99]
|
|
248
|
+
|
|
249
|
+
# Custom print function (e.g., for logging)
|
|
250
|
+
def my_logger(msg):
|
|
251
|
+
print(f"[LOG] {msg}")
|
|
252
|
+
|
|
253
|
+
print_list(items, style='bullet', print_func=my_logger)
|
|
254
|
+
|
|
255
|
+
# Combine partial with custom parameters
|
|
256
|
+
custom_compact = print_list(style='compact', sep=' | ')
|
|
257
|
+
custom_compact(items) # Prints: apple | banana | cherry | date | elderberry | fig
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
### Key Features
|
|
261
|
+
|
|
262
|
+
- **Multiple Styles**: `wrapped`, `columns`, `numbered`, `bullet`, `compact`, `table`
|
|
263
|
+
- **Flexible Output**: Print directly or return strings with `print_func=None`
|
|
264
|
+
- **Partial Functions**: Create reusable printers with pre-configured settings
|
|
265
|
+
- **Customizable**: Control width, separators, line prefixes, and more
|
|
266
|
+
- **Type Safe**: Uses `Literal` types for style validation
|
|
267
|
+
- **Self-Contained**: No external dependencies beyond Python standard library
|
|
268
|
+
|
|
269
|
+
The `print_list` function is perfect for debugging, logging, user interfaces, and any situation where you need to display lists in a readable format.
|
|
@@ -156,3 +156,103 @@ import_object(dot_path: str)
|
|
|
156
156
|
>>> f is join
|
|
157
157
|
True
|
|
158
158
|
```
|
|
159
|
+
|
|
160
|
+
## Pretty Printing Lists
|
|
161
|
+
|
|
162
|
+
The `print_list` function provides flexible, human-friendly ways to display lists and collections. It supports multiple display styles and can be used in several ways.
|
|
163
|
+
|
|
164
|
+
### Basic Usage
|
|
165
|
+
|
|
166
|
+
```python
|
|
167
|
+
from lkj.strings import print_list
|
|
168
|
+
|
|
169
|
+
items = ['apple', 'banana', 'cherry', 'date', 'elderberry', 'fig']
|
|
170
|
+
|
|
171
|
+
# Different display styles
|
|
172
|
+
print_list(items, style='wrapped') # Automatic line wrapping
|
|
173
|
+
print_list(items, style='columns') # Column format
|
|
174
|
+
print_list(items, style='numbered') # Numbered list
|
|
175
|
+
print_list(items, style='bullet') # Bullet points
|
|
176
|
+
print_list(items, style='compact') # All on one line
|
|
177
|
+
print_list(items, style='table') # Table format
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
### Direct Usage with Customization
|
|
181
|
+
|
|
182
|
+
```python
|
|
183
|
+
# Customize width, separators, and formatting
|
|
184
|
+
print_list(items, style='wrapped', max_width=40, sep=' | ')
|
|
185
|
+
print_list(items, style='columns', items_per_line=3)
|
|
186
|
+
print_list(items, style='numbered', line_prefix=' ')
|
|
187
|
+
print_list(items, style='bullet', show_count=False)
|
|
188
|
+
|
|
189
|
+
# Return string instead of printing
|
|
190
|
+
result = print_list(items, style='numbered', print_func=None)
|
|
191
|
+
print(result)
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
### Partial Function Factory
|
|
195
|
+
|
|
196
|
+
When you don't specify the `items` parameter, `print_list` returns a partial function that you can reuse:
|
|
197
|
+
|
|
198
|
+
```python
|
|
199
|
+
# Create specialized printers
|
|
200
|
+
numbered_printer = print_list(style='numbered', show_count=False)
|
|
201
|
+
bullet_printer = print_list(style='bullet', print_func=None)
|
|
202
|
+
compact_printer = print_list(style='compact', max_width=60)
|
|
203
|
+
|
|
204
|
+
# Reuse with different data
|
|
205
|
+
numbered_printer(['a', 'b', 'c']) # Prints: 1. a\n2. b\n3. c
|
|
206
|
+
result = bullet_printer(['x', 'y', 'z']) # Returns: '• x\n• y\n• z'
|
|
207
|
+
compact_printer(['item1', 'item2']) # Prints: item1, item2
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
### Convenience Methods
|
|
211
|
+
|
|
212
|
+
The `print_list` object provides convenient pre-configured methods:
|
|
213
|
+
|
|
214
|
+
```python
|
|
215
|
+
# Quick access to common styles
|
|
216
|
+
print_list.compact(items) # Compact format, no count
|
|
217
|
+
print_list.wrapped(items) # Wrapped format, no count
|
|
218
|
+
print_list.columns(items) # Column format, no count
|
|
219
|
+
print_list.numbered(items) # Numbered format, no count
|
|
220
|
+
print_list.bullets(items) # Bullet format, no count
|
|
221
|
+
|
|
222
|
+
# Specialized methods
|
|
223
|
+
print_list.as_table(data) # Table with headers
|
|
224
|
+
print_list.summary(items) # Summary for long lists
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
### Advanced Examples
|
|
228
|
+
|
|
229
|
+
```python
|
|
230
|
+
# Table with custom data
|
|
231
|
+
data = [['Name', 'Age', 'City'], ['Alice', 25, 'NYC'], ['Bob', 30, 'LA']]
|
|
232
|
+
print_list.as_table(data)
|
|
233
|
+
|
|
234
|
+
# Summary for long lists
|
|
235
|
+
long_list = list(range(100))
|
|
236
|
+
print_list.summary(long_list, max_items=6) # Shows: [0, 1, 2, ..., 97, 98, 99]
|
|
237
|
+
|
|
238
|
+
# Custom print function (e.g., for logging)
|
|
239
|
+
def my_logger(msg):
|
|
240
|
+
print(f"[LOG] {msg}")
|
|
241
|
+
|
|
242
|
+
print_list(items, style='bullet', print_func=my_logger)
|
|
243
|
+
|
|
244
|
+
# Combine partial with custom parameters
|
|
245
|
+
custom_compact = print_list(style='compact', sep=' | ')
|
|
246
|
+
custom_compact(items) # Prints: apple | banana | cherry | date | elderberry | fig
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
### Key Features
|
|
250
|
+
|
|
251
|
+
- **Multiple Styles**: `wrapped`, `columns`, `numbered`, `bullet`, `compact`, `table`
|
|
252
|
+
- **Flexible Output**: Print directly or return strings with `print_func=None`
|
|
253
|
+
- **Partial Functions**: Create reusable printers with pre-configured settings
|
|
254
|
+
- **Customizable**: Control width, separators, line prefixes, and more
|
|
255
|
+
- **Type Safe**: Uses `Literal` types for style validation
|
|
256
|
+
- **Self-Contained**: No external dependencies beyond Python standard library
|
|
257
|
+
|
|
258
|
+
The `print_list` function is perfect for debugging, logging, user interfaces, and any situation where you need to display lists in a readable format.
|
|
@@ -17,6 +17,7 @@ from lkj.dicts import (
|
|
|
17
17
|
)
|
|
18
18
|
from lkj.filesys import get_app_data_dir, get_watermarked_dir, enable_sourcing_from_file
|
|
19
19
|
from lkj.strings import (
|
|
20
|
+
print_list, # Print a list in a nice format (or get a string to process yourself)
|
|
20
21
|
FindReplaceTool, # Tool for finding and replacing substrings in a string
|
|
21
22
|
indent_lines, # Indent all lines of a string
|
|
22
23
|
most_common_indent, # Get the most common indent of a multiline string
|
|
@@ -1,6 +1,49 @@
|
|
|
1
|
-
"""
|
|
1
|
+
"""
|
|
2
|
+
String Utilities Module
|
|
3
|
+
|
|
4
|
+
This module provides a comprehensive set of utility functions and classes for working with strings in Python.
|
|
5
|
+
It includes tools for string manipulation, formatting, pretty-printing, and find/replace operations.
|
|
6
|
+
|
|
7
|
+
Core Components:
|
|
8
|
+
|
|
9
|
+
- StringAppender: A helper class for collecting strings, useful for capturing output that would otherwise be printed.
|
|
10
|
+
- indent_lines: Indents each line of a string by a specified prefix.
|
|
11
|
+
- most_common_indent: Determines the most common indentation used in a multi-line string.
|
|
12
|
+
- FindReplaceTool: A class for advanced find-and-replace operations on strings, supporting regular expressions, match history, and undo functionality.
|
|
13
|
+
|
|
14
|
+
Pretty-Printing Functions:
|
|
15
|
+
|
|
16
|
+
- print_list: Prints lists in various human-friendly formats (wrapped, columns, numbered, bullet, table, compact), with options for width, separators, and custom print functions.
|
|
17
|
+
- print_list.as_table: Formats and prints a list (or list of lists) as a table, with optional headers and alignment.
|
|
18
|
+
- print_list.summary: Prints a summary of a list, showing first few and last few items if the list is long.
|
|
19
|
+
- print_list.compact, print_list.wrapped, print_list.columns, print_list.numbered, print_list.bullets: Convenience methods using print_list's partial functionality for common display styles.
|
|
20
|
+
|
|
21
|
+
These utilities are designed to make it easier to display, format, and manipulate strings and collections of strings in a readable and flexible way.
|
|
22
|
+
"""
|
|
2
23
|
|
|
3
24
|
import re
|
|
25
|
+
from typing import Iterable, Sequence, Callable, Optional, Any, Literal
|
|
26
|
+
from functools import partial
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class StringAppender:
|
|
30
|
+
"""Helper class to collect strings instead of printing them directly."""
|
|
31
|
+
|
|
32
|
+
def __init__(self, separator="\n"):
|
|
33
|
+
self.lines = []
|
|
34
|
+
self.separator = separator
|
|
35
|
+
|
|
36
|
+
def __call__(self, text):
|
|
37
|
+
"""Append text to the internal list."""
|
|
38
|
+
self.lines.append(str(text))
|
|
39
|
+
|
|
40
|
+
def __str__(self):
|
|
41
|
+
"""Return the collected string."""
|
|
42
|
+
return self.separator.join(self.lines)
|
|
43
|
+
|
|
44
|
+
def get_string(self):
|
|
45
|
+
"""Alternative way to get the string."""
|
|
46
|
+
return str(self)
|
|
4
47
|
|
|
5
48
|
|
|
6
49
|
def indent_lines(string: str, indent: str, *, line_sep="\n") -> str:
|
|
@@ -686,3 +729,446 @@ class FindReplaceTool:
|
|
|
686
729
|
self._text_versions.pop()
|
|
687
730
|
steps -= 1
|
|
688
731
|
return self.get_modified_text()
|
|
732
|
+
|
|
733
|
+
|
|
734
|
+
def print_list(
|
|
735
|
+
items: Optional[Iterable[Any]] = None,
|
|
736
|
+
*,
|
|
737
|
+
style: Literal[
|
|
738
|
+
"wrapped", "columns", "numbered", "bullet", "table", "compact"
|
|
739
|
+
] = "wrapped",
|
|
740
|
+
max_width: int = 80,
|
|
741
|
+
sep: str = ", ",
|
|
742
|
+
line_prefix: str = "",
|
|
743
|
+
items_per_line=None,
|
|
744
|
+
show_count=True,
|
|
745
|
+
title=None,
|
|
746
|
+
print_func=print,
|
|
747
|
+
):
|
|
748
|
+
"""
|
|
749
|
+
Print a list in a nice, readable format with multiple style options.
|
|
750
|
+
|
|
751
|
+
Args:
|
|
752
|
+
items: The list or iterable to print. If None, returns a partial function.
|
|
753
|
+
style: One of "wrapped", "columns", "numbered", "bullet", "table", "compact"
|
|
754
|
+
max_width: Maximum width for wrapped style
|
|
755
|
+
sep: Separator for items
|
|
756
|
+
line_prefix: Prefix for each line
|
|
757
|
+
items_per_line: For columns style, how many items per line
|
|
758
|
+
show_count: Whether to show the count of items
|
|
759
|
+
title: Optional title to display before the list
|
|
760
|
+
print_func: Function to use for printing. Defaults to print.
|
|
761
|
+
If None, returns the string instead of printing.
|
|
762
|
+
|
|
763
|
+
Examples:
|
|
764
|
+
>>> items = ["apple", "banana", "cherry", "date", "elderberry", "fig"]
|
|
765
|
+
|
|
766
|
+
# Wrapped style (default)
|
|
767
|
+
>>> print_list(items, max_width=30)
|
|
768
|
+
List (6 items):
|
|
769
|
+
apple, banana, cherry, date,
|
|
770
|
+
elderberry, fig
|
|
771
|
+
|
|
772
|
+
# Columns style
|
|
773
|
+
>>> print_list(items, style="columns", items_per_line=3)
|
|
774
|
+
List (6 items):
|
|
775
|
+
apple banana cherry
|
|
776
|
+
date elderberry fig
|
|
777
|
+
|
|
778
|
+
# Numbered style
|
|
779
|
+
>>> print_list(items, style="numbered")
|
|
780
|
+
List (6 items):
|
|
781
|
+
1. apple
|
|
782
|
+
2. banana
|
|
783
|
+
3. cherry
|
|
784
|
+
4. date
|
|
785
|
+
5. elderberry
|
|
786
|
+
6. fig
|
|
787
|
+
|
|
788
|
+
# Bullet style
|
|
789
|
+
>>> print_list(items, style="bullet")
|
|
790
|
+
List (6 items):
|
|
791
|
+
• apple
|
|
792
|
+
• banana
|
|
793
|
+
• cherry
|
|
794
|
+
• date
|
|
795
|
+
• elderberry
|
|
796
|
+
• fig
|
|
797
|
+
|
|
798
|
+
# Return string instead of printing
|
|
799
|
+
>>> result = print_list(items, style="numbered", print_func=None)
|
|
800
|
+
>>> print(result)
|
|
801
|
+
List (6 items):
|
|
802
|
+
1. apple
|
|
803
|
+
2. banana
|
|
804
|
+
3. cherry
|
|
805
|
+
4. date
|
|
806
|
+
5. elderberry
|
|
807
|
+
6. fig
|
|
808
|
+
|
|
809
|
+
Partial function functionality: If you don't specify the items (or items=None),
|
|
810
|
+
the function returns a partial function that can be called with the items later.
|
|
811
|
+
That is, the print_list acts as a factory function for different
|
|
812
|
+
printing styles.
|
|
813
|
+
|
|
814
|
+
>>> numbered_printer = print_list(style="numbered", show_count=False)
|
|
815
|
+
>>> numbered_printer(items)
|
|
816
|
+
1. apple
|
|
817
|
+
2. banana
|
|
818
|
+
3. cherry
|
|
819
|
+
4. date
|
|
820
|
+
5. elderberry
|
|
821
|
+
6. fig
|
|
822
|
+
|
|
823
|
+
>>> compact_printer = print_list(style="compact", max_width=60, show_count=False)
|
|
824
|
+
>>> compact_printer(items)
|
|
825
|
+
apple, banana, cherry, date, elderberry, fig
|
|
826
|
+
|
|
827
|
+
>>> bullet_printer = print_list(style="bullet", print_func=None, show_count=False)
|
|
828
|
+
>>> result = bullet_printer(items)
|
|
829
|
+
>>> print(result)
|
|
830
|
+
• apple
|
|
831
|
+
• banana
|
|
832
|
+
• cherry
|
|
833
|
+
• date
|
|
834
|
+
• elderberry
|
|
835
|
+
• fig
|
|
836
|
+
"""
|
|
837
|
+
if items is None:
|
|
838
|
+
return partial(
|
|
839
|
+
print_list,
|
|
840
|
+
style=style,
|
|
841
|
+
max_width=max_width,
|
|
842
|
+
sep=sep,
|
|
843
|
+
line_prefix=line_prefix,
|
|
844
|
+
items_per_line=items_per_line,
|
|
845
|
+
show_count=show_count,
|
|
846
|
+
title=title,
|
|
847
|
+
print_func=print_func,
|
|
848
|
+
)
|
|
849
|
+
items = list(items) # Convert to list if it's an iterable
|
|
850
|
+
|
|
851
|
+
# Handle print_func=None by using StringAppender
|
|
852
|
+
if print_func is None:
|
|
853
|
+
string_appender = StringAppender()
|
|
854
|
+
print_func = string_appender
|
|
855
|
+
return_string = True
|
|
856
|
+
else:
|
|
857
|
+
return_string = False
|
|
858
|
+
|
|
859
|
+
# Show title and count
|
|
860
|
+
if title:
|
|
861
|
+
print_func(title)
|
|
862
|
+
elif show_count:
|
|
863
|
+
print_func(f"List ({len(items)} items):")
|
|
864
|
+
|
|
865
|
+
if not items:
|
|
866
|
+
print_func(f"{line_prefix}(empty list)")
|
|
867
|
+
return str(string_appender) if return_string else None
|
|
868
|
+
|
|
869
|
+
if style == "wrapped":
|
|
870
|
+
# Use the existing wrapped_print function with a safe fallback for doctest context
|
|
871
|
+
try:
|
|
872
|
+
from .loggers import wrapped_print # type: ignore
|
|
873
|
+
except Exception: # pragma: no cover - fallback when relative import fails
|
|
874
|
+
import textwrap
|
|
875
|
+
|
|
876
|
+
def wrapped_print(
|
|
877
|
+
items, sep=", ", max_width=80, line_prefix="", print_func=print
|
|
878
|
+
):
|
|
879
|
+
text = sep.join(map(str, items))
|
|
880
|
+
return print_func(
|
|
881
|
+
line_prefix
|
|
882
|
+
+ textwrap.fill(
|
|
883
|
+
text, width=max_width, subsequent_indent=line_prefix
|
|
884
|
+
)
|
|
885
|
+
)
|
|
886
|
+
|
|
887
|
+
wrapped_print(
|
|
888
|
+
items,
|
|
889
|
+
sep=sep,
|
|
890
|
+
max_width=max_width,
|
|
891
|
+
line_prefix=line_prefix,
|
|
892
|
+
print_func=print_func,
|
|
893
|
+
)
|
|
894
|
+
|
|
895
|
+
elif style == "columns":
|
|
896
|
+
if items_per_line is None:
|
|
897
|
+
# Auto-calculate based on max_width and average item length
|
|
898
|
+
avg_length = sum(len(str(item)) for item in items) / len(items)
|
|
899
|
+
items_per_line = max(1, int(max_width / (avg_length + len(sep))))
|
|
900
|
+
|
|
901
|
+
for i in range(0, len(items), items_per_line):
|
|
902
|
+
line_items = items[i : i + items_per_line]
|
|
903
|
+
# Calculate column widths across all rows for each column position
|
|
904
|
+
col_widths = []
|
|
905
|
+
for col in range(items_per_line):
|
|
906
|
+
col_items = items[col::items_per_line]
|
|
907
|
+
if col_items:
|
|
908
|
+
col_widths.append(max(len(str(item)) for item in col_items))
|
|
909
|
+
else:
|
|
910
|
+
col_widths.append(0)
|
|
911
|
+
|
|
912
|
+
# Print the line; pad all but the last column to avoid trailing spaces
|
|
913
|
+
parts = []
|
|
914
|
+
for j, item in enumerate(line_items):
|
|
915
|
+
text = str(item)
|
|
916
|
+
if j < len(line_items) - 1:
|
|
917
|
+
parts.append(text.ljust(col_widths[j]))
|
|
918
|
+
else:
|
|
919
|
+
parts.append(text)
|
|
920
|
+
print_func(f"{line_prefix}{' '.join(parts)}")
|
|
921
|
+
|
|
922
|
+
elif style == "numbered":
|
|
923
|
+
max_num_width = len(str(len(items)))
|
|
924
|
+
for i, item in enumerate(items, 1):
|
|
925
|
+
print_func(f"{line_prefix}{i:>{max_num_width}}. {item}")
|
|
926
|
+
|
|
927
|
+
elif style == "bullet":
|
|
928
|
+
for item in items:
|
|
929
|
+
print_func(f"{line_prefix}• {item}")
|
|
930
|
+
|
|
931
|
+
elif style == "table":
|
|
932
|
+
# Simple table format
|
|
933
|
+
if items and hasattr(items[0], "__iter__") and not isinstance(items[0], str):
|
|
934
|
+
# List of lists/tuples - treat as table data
|
|
935
|
+
if not items:
|
|
936
|
+
return str(string_appender) if return_string else None
|
|
937
|
+
|
|
938
|
+
# Find column widths
|
|
939
|
+
num_cols = len(items[0])
|
|
940
|
+
col_widths = [0] * num_cols
|
|
941
|
+
for row in items:
|
|
942
|
+
for j, cell in enumerate(row):
|
|
943
|
+
col_widths[j] = max(col_widths[j], len(str(cell)))
|
|
944
|
+
|
|
945
|
+
# Print table
|
|
946
|
+
for row in items:
|
|
947
|
+
formatted_row = []
|
|
948
|
+
for j, cell in enumerate(row):
|
|
949
|
+
formatted_row.append(str(cell).ljust(col_widths[j]))
|
|
950
|
+
print_func(f"{line_prefix}{' | '.join(formatted_row)}")
|
|
951
|
+
else:
|
|
952
|
+
# Single column table
|
|
953
|
+
max_width = max(len(str(item)) for item in items)
|
|
954
|
+
for item in items:
|
|
955
|
+
print_func(f"{line_prefix}{str(item).ljust(max_width)}")
|
|
956
|
+
|
|
957
|
+
elif style == "compact":
|
|
958
|
+
# Most compact form - all on one line if possible
|
|
959
|
+
items_str = sep.join(str(item) for item in items)
|
|
960
|
+
if len(items_str) <= max_width:
|
|
961
|
+
print_func(f"{line_prefix}{items_str}")
|
|
962
|
+
else:
|
|
963
|
+
# Fall back to wrapped style
|
|
964
|
+
try:
|
|
965
|
+
from .loggers import wrapped_print # type: ignore
|
|
966
|
+
except Exception: # pragma: no cover - fallback when relative import fails
|
|
967
|
+
import textwrap
|
|
968
|
+
|
|
969
|
+
def wrapped_print(
|
|
970
|
+
items, sep=", ", max_width=80, line_prefix="", print_func=print
|
|
971
|
+
):
|
|
972
|
+
text = sep.join(map(str, items))
|
|
973
|
+
return print_func(
|
|
974
|
+
line_prefix
|
|
975
|
+
+ textwrap.fill(
|
|
976
|
+
text, width=max_width, subsequent_indent=line_prefix
|
|
977
|
+
)
|
|
978
|
+
)
|
|
979
|
+
|
|
980
|
+
wrapped_print(
|
|
981
|
+
items,
|
|
982
|
+
sep=sep,
|
|
983
|
+
max_width=max_width,
|
|
984
|
+
line_prefix=line_prefix,
|
|
985
|
+
print_func=print_func,
|
|
986
|
+
)
|
|
987
|
+
|
|
988
|
+
else:
|
|
989
|
+
raise ValueError(
|
|
990
|
+
f"Unknown style: {style}. Use one of: wrapped, columns, numbered, bullet, table, compact"
|
|
991
|
+
)
|
|
992
|
+
|
|
993
|
+
return str(string_appender) if return_string else None
|
|
994
|
+
|
|
995
|
+
|
|
996
|
+
def print_list_as_table(
|
|
997
|
+
items, headers=None, *, max_width=80, align="left", print_func=print
|
|
998
|
+
):
|
|
999
|
+
"""
|
|
1000
|
+
Print a list as a nicely formatted table.
|
|
1001
|
+
|
|
1002
|
+
Args:
|
|
1003
|
+
items: List of items (strings, numbers, or objects with __str__)
|
|
1004
|
+
headers: Optional list of column headers
|
|
1005
|
+
max_width: Maximum width of the table
|
|
1006
|
+
align: Alignment for columns ("left", "right", "center")
|
|
1007
|
+
print_func: Function to use for printing. Defaults to print.
|
|
1008
|
+
If None, returns the string instead of printing.
|
|
1009
|
+
|
|
1010
|
+
Examples:
|
|
1011
|
+
>>> data = [["Name", "Age", "City"], ["Alice", 25, "NYC"], ["Bob", 30, "LA"]]
|
|
1012
|
+
>>> print_list_as_table(data)
|
|
1013
|
+
Name | Age | City
|
|
1014
|
+
-----|---|----
|
|
1015
|
+
Alice | 25 | NYC
|
|
1016
|
+
Bob | 30 | LA
|
|
1017
|
+
|
|
1018
|
+
# Return string instead of printing
|
|
1019
|
+
>>> result = print_list_as_table(data, print_func=None)
|
|
1020
|
+
>>> print(result)
|
|
1021
|
+
Name | Age | City
|
|
1022
|
+
-----|---|----
|
|
1023
|
+
Alice | 25 | NYC
|
|
1024
|
+
Bob | 30 | LA
|
|
1025
|
+
"""
|
|
1026
|
+
# Handle print_func=None by using StringAppender
|
|
1027
|
+
if print_func is None:
|
|
1028
|
+
string_appender = StringAppender()
|
|
1029
|
+
print_func = string_appender
|
|
1030
|
+
return_string = True
|
|
1031
|
+
else:
|
|
1032
|
+
return_string = False
|
|
1033
|
+
|
|
1034
|
+
if not items:
|
|
1035
|
+
print_func("(empty table)")
|
|
1036
|
+
return str(string_appender) if return_string else None
|
|
1037
|
+
|
|
1038
|
+
# Convert items to list of lists if needed
|
|
1039
|
+
if not hasattr(items[0], "__iter__") or isinstance(items[0], str):
|
|
1040
|
+
# Single column
|
|
1041
|
+
table_data = [[str(item)] for item in items]
|
|
1042
|
+
if headers:
|
|
1043
|
+
headers = [headers] if isinstance(headers, str) else headers
|
|
1044
|
+
else:
|
|
1045
|
+
headers = ["Value"]
|
|
1046
|
+
else:
|
|
1047
|
+
# Multi-column
|
|
1048
|
+
table_data = [[str(cell) for cell in row] for row in items]
|
|
1049
|
+
|
|
1050
|
+
if headers:
|
|
1051
|
+
table_data.insert(0, headers)
|
|
1052
|
+
|
|
1053
|
+
# Calculate column widths
|
|
1054
|
+
num_cols = len(table_data[0])
|
|
1055
|
+
col_widths = [0] * num_cols
|
|
1056
|
+
|
|
1057
|
+
for row in table_data:
|
|
1058
|
+
for j, cell in enumerate(row):
|
|
1059
|
+
col_widths[j] = max(col_widths[j], len(cell))
|
|
1060
|
+
|
|
1061
|
+
# Adjust column widths to fit max_width
|
|
1062
|
+
total_width = sum(col_widths) + (num_cols - 1) * 3 # 3 for " | "
|
|
1063
|
+
if total_width > max_width:
|
|
1064
|
+
# Reduce column widths proportionally
|
|
1065
|
+
excess = total_width - max_width
|
|
1066
|
+
for j in range(num_cols):
|
|
1067
|
+
reduction = min(excess // num_cols, col_widths[j] // 2)
|
|
1068
|
+
col_widths[j] -= reduction
|
|
1069
|
+
excess -= reduction
|
|
1070
|
+
|
|
1071
|
+
# Determine if the first row should be treated as header
|
|
1072
|
+
header_present = bool(headers) or all(isinstance(c, str) for c in table_data[0])
|
|
1073
|
+
|
|
1074
|
+
# Helper to format a row without padding the last column
|
|
1075
|
+
def format_row(row):
|
|
1076
|
+
formatted = []
|
|
1077
|
+
for j, cell in enumerate(row):
|
|
1078
|
+
if j < num_cols - 1:
|
|
1079
|
+
formatted.append(cell.ljust(col_widths[j]))
|
|
1080
|
+
else:
|
|
1081
|
+
formatted.append(cell)
|
|
1082
|
+
return " | ".join(formatted)
|
|
1083
|
+
|
|
1084
|
+
for i, row in enumerate(table_data):
|
|
1085
|
+
print_func(format_row(row))
|
|
1086
|
+
if header_present and i == 0:
|
|
1087
|
+
# Print separator line without spaces around the pipes
|
|
1088
|
+
print_func("|".join("-" * w for w in col_widths))
|
|
1089
|
+
|
|
1090
|
+
return str(string_appender) if return_string else None
|
|
1091
|
+
|
|
1092
|
+
|
|
1093
|
+
def print_list_summary(
|
|
1094
|
+
items, *, max_items=10, show_total=True, title=None, print_func=print
|
|
1095
|
+
):
|
|
1096
|
+
"""
|
|
1097
|
+
Print a summary of a list, showing first few and last few items if the list is long.
|
|
1098
|
+
|
|
1099
|
+
Args:
|
|
1100
|
+
items: The list to summarize
|
|
1101
|
+
max_items: Maximum number of items to show (first + last)
|
|
1102
|
+
show_total: Whether to show the total count
|
|
1103
|
+
title: Optional title
|
|
1104
|
+
print_func: Function to use for printing. Defaults to print.
|
|
1105
|
+
If None, returns the string instead of printing.
|
|
1106
|
+
|
|
1107
|
+
Examples:
|
|
1108
|
+
>>> long_list = list(range(100))
|
|
1109
|
+
>>> print_list_summary(long_list, max_items=6)
|
|
1110
|
+
List (100 items):
|
|
1111
|
+
[0, 1, 2, ..., 97, 98, 99]
|
|
1112
|
+
|
|
1113
|
+
>>> print_list_summary(long_list, max_items=10)
|
|
1114
|
+
List (100 items):
|
|
1115
|
+
[0, 1, 2, 3, 4, ..., 95, 96, 97, 98, 99]
|
|
1116
|
+
|
|
1117
|
+
# Return string instead of printing
|
|
1118
|
+
>>> result = print_list_summary(long_list, max_items=6, print_func=None)
|
|
1119
|
+
>>> print(result)
|
|
1120
|
+
List (100 items):
|
|
1121
|
+
[0, 1, 2, ..., 97, 98, 99]
|
|
1122
|
+
"""
|
|
1123
|
+
items = list(items)
|
|
1124
|
+
|
|
1125
|
+
# Handle print_func=None by using StringAppender
|
|
1126
|
+
if print_func is None:
|
|
1127
|
+
string_appender = StringAppender()
|
|
1128
|
+
print_func = string_appender
|
|
1129
|
+
return_string = True
|
|
1130
|
+
else:
|
|
1131
|
+
return_string = False
|
|
1132
|
+
|
|
1133
|
+
if title:
|
|
1134
|
+
print_func(title)
|
|
1135
|
+
elif show_total:
|
|
1136
|
+
print_func(f"List ({len(items)} items):")
|
|
1137
|
+
|
|
1138
|
+
if not items:
|
|
1139
|
+
print_func("(empty list)")
|
|
1140
|
+
return str(string_appender) if return_string else None
|
|
1141
|
+
|
|
1142
|
+
if len(items) <= max_items:
|
|
1143
|
+
print_func(items)
|
|
1144
|
+
else:
|
|
1145
|
+
# Show first and last items with ellipsis
|
|
1146
|
+
first_count = max_items // 2
|
|
1147
|
+
last_count = max_items - first_count
|
|
1148
|
+
|
|
1149
|
+
first_items = items[:first_count]
|
|
1150
|
+
last_items = items[-last_count:]
|
|
1151
|
+
|
|
1152
|
+
print_func(
|
|
1153
|
+
f"[{', '.join(map(str, first_items))}, ..., {', '.join(map(str, last_items))}]"
|
|
1154
|
+
)
|
|
1155
|
+
|
|
1156
|
+
return str(string_appender) if return_string else None
|
|
1157
|
+
|
|
1158
|
+
|
|
1159
|
+
# Convenience functions are now available as attributes of print_list
|
|
1160
|
+
# using the partial functionality:
|
|
1161
|
+
# - print_list.compact
|
|
1162
|
+
# - print_list.wrapped
|
|
1163
|
+
# - print_list.columns
|
|
1164
|
+
# - print_list.numbered
|
|
1165
|
+
# - print_list.bullets
|
|
1166
|
+
|
|
1167
|
+
|
|
1168
|
+
print_list.as_table = print_list_as_table
|
|
1169
|
+
print_list.summary = print_list_summary
|
|
1170
|
+
print_list.compact = print_list(style="compact", show_count=False)
|
|
1171
|
+
print_list.wrapped = print_list(style="wrapped", show_count=False)
|
|
1172
|
+
print_list.columns = print_list(style="columns", show_count=False)
|
|
1173
|
+
print_list.numbered = print_list(style="numbered", show_count=False)
|
|
1174
|
+
print_list.bullets = print_list(style="bullet", show_count=False)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: lkj
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.45
|
|
4
4
|
Summary: A dump of homeless useful utils
|
|
5
5
|
Home-page: https://github.com/thorwhalen/lkj
|
|
6
6
|
Author: Thor Whalen
|
|
@@ -167,3 +167,103 @@ import_object(dot_path: str)
|
|
|
167
167
|
>>> f is join
|
|
168
168
|
True
|
|
169
169
|
```
|
|
170
|
+
|
|
171
|
+
## Pretty Printing Lists
|
|
172
|
+
|
|
173
|
+
The `print_list` function provides flexible, human-friendly ways to display lists and collections. It supports multiple display styles and can be used in several ways.
|
|
174
|
+
|
|
175
|
+
### Basic Usage
|
|
176
|
+
|
|
177
|
+
```python
|
|
178
|
+
from lkj.strings import print_list
|
|
179
|
+
|
|
180
|
+
items = ['apple', 'banana', 'cherry', 'date', 'elderberry', 'fig']
|
|
181
|
+
|
|
182
|
+
# Different display styles
|
|
183
|
+
print_list(items, style='wrapped') # Automatic line wrapping
|
|
184
|
+
print_list(items, style='columns') # Column format
|
|
185
|
+
print_list(items, style='numbered') # Numbered list
|
|
186
|
+
print_list(items, style='bullet') # Bullet points
|
|
187
|
+
print_list(items, style='compact') # All on one line
|
|
188
|
+
print_list(items, style='table') # Table format
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
### Direct Usage with Customization
|
|
192
|
+
|
|
193
|
+
```python
|
|
194
|
+
# Customize width, separators, and formatting
|
|
195
|
+
print_list(items, style='wrapped', max_width=40, sep=' | ')
|
|
196
|
+
print_list(items, style='columns', items_per_line=3)
|
|
197
|
+
print_list(items, style='numbered', line_prefix=' ')
|
|
198
|
+
print_list(items, style='bullet', show_count=False)
|
|
199
|
+
|
|
200
|
+
# Return string instead of printing
|
|
201
|
+
result = print_list(items, style='numbered', print_func=None)
|
|
202
|
+
print(result)
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
### Partial Function Factory
|
|
206
|
+
|
|
207
|
+
When you don't specify the `items` parameter, `print_list` returns a partial function that you can reuse:
|
|
208
|
+
|
|
209
|
+
```python
|
|
210
|
+
# Create specialized printers
|
|
211
|
+
numbered_printer = print_list(style='numbered', show_count=False)
|
|
212
|
+
bullet_printer = print_list(style='bullet', print_func=None)
|
|
213
|
+
compact_printer = print_list(style='compact', max_width=60)
|
|
214
|
+
|
|
215
|
+
# Reuse with different data
|
|
216
|
+
numbered_printer(['a', 'b', 'c']) # Prints: 1. a\n2. b\n3. c
|
|
217
|
+
result = bullet_printer(['x', 'y', 'z']) # Returns: '• x\n• y\n• z'
|
|
218
|
+
compact_printer(['item1', 'item2']) # Prints: item1, item2
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
### Convenience Methods
|
|
222
|
+
|
|
223
|
+
The `print_list` object provides convenient pre-configured methods:
|
|
224
|
+
|
|
225
|
+
```python
|
|
226
|
+
# Quick access to common styles
|
|
227
|
+
print_list.compact(items) # Compact format, no count
|
|
228
|
+
print_list.wrapped(items) # Wrapped format, no count
|
|
229
|
+
print_list.columns(items) # Column format, no count
|
|
230
|
+
print_list.numbered(items) # Numbered format, no count
|
|
231
|
+
print_list.bullets(items) # Bullet format, no count
|
|
232
|
+
|
|
233
|
+
# Specialized methods
|
|
234
|
+
print_list.as_table(data) # Table with headers
|
|
235
|
+
print_list.summary(items) # Summary for long lists
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
### Advanced Examples
|
|
239
|
+
|
|
240
|
+
```python
|
|
241
|
+
# Table with custom data
|
|
242
|
+
data = [['Name', 'Age', 'City'], ['Alice', 25, 'NYC'], ['Bob', 30, 'LA']]
|
|
243
|
+
print_list.as_table(data)
|
|
244
|
+
|
|
245
|
+
# Summary for long lists
|
|
246
|
+
long_list = list(range(100))
|
|
247
|
+
print_list.summary(long_list, max_items=6) # Shows: [0, 1, 2, ..., 97, 98, 99]
|
|
248
|
+
|
|
249
|
+
# Custom print function (e.g., for logging)
|
|
250
|
+
def my_logger(msg):
|
|
251
|
+
print(f"[LOG] {msg}")
|
|
252
|
+
|
|
253
|
+
print_list(items, style='bullet', print_func=my_logger)
|
|
254
|
+
|
|
255
|
+
# Combine partial with custom parameters
|
|
256
|
+
custom_compact = print_list(style='compact', sep=' | ')
|
|
257
|
+
custom_compact(items) # Prints: apple | banana | cherry | date | elderberry | fig
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
### Key Features
|
|
261
|
+
|
|
262
|
+
- **Multiple Styles**: `wrapped`, `columns`, `numbered`, `bullet`, `compact`, `table`
|
|
263
|
+
- **Flexible Output**: Print directly or return strings with `print_func=None`
|
|
264
|
+
- **Partial Functions**: Create reusable printers with pre-configured settings
|
|
265
|
+
- **Customizable**: Control width, separators, line prefixes, and more
|
|
266
|
+
- **Type Safe**: Uses `Literal` types for style validation
|
|
267
|
+
- **Self-Contained**: No external dependencies beyond Python standard library
|
|
268
|
+
|
|
269
|
+
The `print_list` function is perfect for debugging, logging, user interfaces, and any situation where you need to display lists in a readable format.
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|