|
@@
-16,7
+16,20
b' import warnings'
|
|
16
|
from string import Formatter
|
|
16
|
from string import Formatter
|
|
17
|
from pathlib import Path
|
|
17
|
from pathlib import Path
|
|
18
|
|
|
18
|
|
|
19
|
from typing import List, Dict, Tuple, Optional, cast, Sequence, Mapping, Any
|
|
19
|
from typing import (
|
|
|
|
|
20
|
List,
|
|
|
|
|
21
|
Dict,
|
|
|
|
|
22
|
Tuple,
|
|
|
|
|
23
|
Optional,
|
|
|
|
|
24
|
cast,
|
|
|
|
|
25
|
Sequence,
|
|
|
|
|
26
|
Mapping,
|
|
|
|
|
27
|
Any,
|
|
|
|
|
28
|
Union,
|
|
|
|
|
29
|
Callable,
|
|
|
|
|
30
|
Iterator,
|
|
|
|
|
31
|
TypeVar,
|
|
|
|
|
32
|
)
|
|
20
|
|
|
33
|
|
|
21
|
if sys.version_info < (3, 12):
|
|
34
|
if sys.version_info < (3, 12):
|
|
22
|
from typing_extensions import Self
|
|
35
|
from typing_extensions import Self
|
|
@@
-138,8
+151,13
b' class SList(list):'
|
|
138
|
|
|
151
|
|
|
139
|
p = paths = property(get_paths)
|
|
152
|
p = paths = property(get_paths)
|
|
140
|
|
|
153
|
|
|
141
|
def grep(self, pattern, prune = False, field = None):
|
|
154
|
def grep(
|
|
142
|
""" Return all strings matching 'pattern' (a regex or callable)
|
|
155
|
self,
|
|
|
|
|
156
|
pattern: Union[str, Callable[[Any], re.Match[str] | None]],
|
|
|
|
|
157
|
prune: bool = False,
|
|
|
|
|
158
|
field: Optional[int] = None,
|
|
|
|
|
159
|
) -> Self:
|
|
|
|
|
160
|
"""Return all strings matching 'pattern' (a regex or callable)
|
|
143
|
|
|
161
|
|
|
144
|
This is case-insensitive. If prune is true, return all items
|
|
162
|
This is case-insensitive. If prune is true, return all items
|
|
145
|
NOT matching the pattern.
|
|
163
|
NOT matching the pattern.
|
|
@@
-154,7
+172,7
b' class SList(list):'
|
|
154
|
a.grep('chm', field=-1)
|
|
172
|
a.grep('chm', field=-1)
|
|
155
|
"""
|
|
173
|
"""
|
|
156
|
|
|
174
|
|
|
157
|
def match_target(s):
|
|
175
|
def match_target(s: str) -> str:
|
|
158
|
if field is None:
|
|
176
|
if field is None:
|
|
159
|
return s
|
|
177
|
return s
|
|
160
|
parts = s.split()
|
|
178
|
parts = s.split()
|
|
@@
-169,12
+187,12
b' class SList(list):'
|
|
169
|
else:
|
|
187
|
else:
|
|
170
|
pred = pattern
|
|
188
|
pred = pattern
|
|
171
|
if not prune:
|
|
189
|
if not prune:
|
|
172
|
return SList([el for el in self if pred(match_target(el))])
|
|
190
|
return type(self)([el for el in self if pred(match_target(el))])
|
|
173
|
else:
|
|
191
|
else:
|
|
174
|
return SList([el for el in self if not pred(match_target(el))])
|
|
192
|
return type(self)([el for el in self if not pred(match_target(el))])
|
|
175
|
|
|
193
|
|
|
176
|
def fields(self, *fields):
|
|
194
|
def fields(self, *fields: List[str]) -> List[List[str]]:
|
|
177
|
""" Collect whitespace-separated fields from string list
|
|
195
|
"""Collect whitespace-separated fields from string list
|
|
178
|
|
|
196
|
|
|
179
|
Allows quick awk-like usage of string lists.
|
|
197
|
Allows quick awk-like usage of string lists.
|
|
180
|
|
|
198
|
|
|
@@
-209,8
+227,12
b' class SList(list):'
|
|
209
|
|
|
227
|
|
|
210
|
return res
|
|
228
|
return res
|
|
211
|
|
|
229
|
|
|
212
|
def sort(self,field= None, nums = False):
|
|
230
|
def sort( # type:ignore[override]
|
|
213
|
""" sort by specified fields (see fields())
|
|
231
|
self,
|
|
|
|
|
232
|
field: Optional[List[str]] = None,
|
|
|
|
|
233
|
nums: bool = False,
|
|
|
|
|
234
|
) -> Self:
|
|
|
|
|
235
|
"""sort by specified fields (see fields())
|
|
214
|
|
|
236
|
|
|
215
|
Example::
|
|
237
|
Example::
|
|
216
|
|
|
238
|
|
|
@@
-236,7
+258,7
b' class SList(list):'
|
|
236
|
|
|
258
|
|
|
237
|
|
|
259
|
|
|
238
|
dsu.sort()
|
|
260
|
dsu.sort()
|
|
239
|
return SList([t[1] for t in dsu])
|
|
261
|
return type(self)([t[1] for t in dsu])
|
|
240
|
|
|
262
|
|
|
241
|
|
|
263
|
|
|
242
|
# FIXME: We need to reimplement type specific displayhook and then add this
|
|
264
|
# FIXME: We need to reimplement type specific displayhook and then add this
|
|
@@
-255,7
+277,7
b' class SList(list):'
|
|
255
|
# print_slist = result_display.register(SList)(print_slist)
|
|
277
|
# print_slist = result_display.register(SList)(print_slist)
|
|
256
|
|
|
278
|
|
|
257
|
|
|
279
|
|
|
258
|
def indent(instr,nspaces=4, ntabs=0, flatten=False):
|
|
280
|
def indent(instr: str, nspaces: int = 4, ntabs: int = 0, flatten: bool = False) -> str:
|
|
259
|
"""Indent a string a given number of spaces or tabstops.
|
|
281
|
"""Indent a string a given number of spaces or tabstops.
|
|
260
|
|
|
282
|
|
|
261
|
indent(str,nspaces=4,ntabs=0) -> indent str by ntabs+nspaces.
|
|
283
|
indent(str,nspaces=4,ntabs=0) -> indent str by ntabs+nspaces.
|
|
@@
-275,7
+297,7
b' def indent(instr,nspaces=4, ntabs=0, flatten=False):'
|
|
275
|
|
|
297
|
|
|
276
|
Returns
|
|
298
|
Returns
|
|
277
|
-------
|
|
299
|
-------
|
|
278
|
str|unicode : string indented by ntabs and nspaces.
|
|
300
|
str : string indented by ntabs and nspaces.
|
|
279
|
|
|
301
|
|
|
280
|
"""
|
|
302
|
"""
|
|
281
|
if instr is None:
|
|
303
|
if instr is None:
|
|
@@
-292,7
+314,7
b' def indent(instr,nspaces=4, ntabs=0, flatten=False):'
|
|
292
|
return outstr
|
|
314
|
return outstr
|
|
293
|
|
|
315
|
|
|
294
|
|
|
316
|
|
|
295
|
def list_strings(arg):
|
|
317
|
def list_strings(arg: Union[str, List[str]]) -> List[str]:
|
|
296
|
"""Always return a list of strings, given a string or list of strings
|
|
318
|
"""Always return a list of strings, given a string or list of strings
|
|
297
|
as input.
|
|
319
|
as input.
|
|
298
|
|
|
320
|
|
|
@@
-316,7
+338,7
b' def list_strings(arg):'
|
|
316
|
return arg
|
|
338
|
return arg
|
|
317
|
|
|
339
|
|
|
318
|
|
|
340
|
|
|
319
|
def marquee(txt='',width=78,mark='*'):
|
|
341
|
def marquee(txt: str = "", width: int = 78, mark: str = "*") -> str:
|
|
320
|
"""Return the input string centered in a 'marquee'.
|
|
342
|
"""Return the input string centered in a 'marquee'.
|
|
321
|
|
|
343
|
|
|
322
|
Examples
|
|
344
|
Examples
|
|
@@
-343,7
+365,8
b" def marquee(txt='',width=78,mark='*'):"
|
|
343
|
|
|
365
|
|
|
344
|
ini_spaces_re = re.compile(r'^(\s+)')
|
|
366
|
ini_spaces_re = re.compile(r'^(\s+)')
|
|
345
|
|
|
367
|
|
|
346
|
def num_ini_spaces(strng):
|
|
368
|
|
|
|
|
|
369
|
def num_ini_spaces(strng: str) -> int:
|
|
347
|
"""Return the number of initial spaces in a string"""
|
|
370
|
"""Return the number of initial spaces in a string"""
|
|
348
|
warnings.warn(
|
|
371
|
warnings.warn(
|
|
349
|
"`num_ini_spaces` is Pending Deprecation since IPython 8.17."
|
|
372
|
"`num_ini_spaces` is Pending Deprecation since IPython 8.17."
|
|
@@
-359,7
+382,7
b' def num_ini_spaces(strng):'
|
|
359
|
return 0
|
|
382
|
return 0
|
|
360
|
|
|
383
|
|
|
361
|
|
|
384
|
|
|
362
|
def format_screen(strng):
|
|
385
|
def format_screen(strng: str) -> str:
|
|
363
|
"""Format a string for screen printing.
|
|
386
|
"""Format a string for screen printing.
|
|
364
|
|
|
387
|
|
|
365
|
This removes some latex-type format codes."""
|
|
388
|
This removes some latex-type format codes."""
|
|
@@
-396,7
+419,7
b' def dedent(text: str) -> str:'
|
|
396
|
return '\n'.join([first, rest])
|
|
419
|
return '\n'.join([first, rest])
|
|
397
|
|
|
420
|
|
|
398
|
|
|
421
|
|
|
399
|
def wrap_paragraphs(text, ncols=80):
|
|
422
|
def wrap_paragraphs(text: str, ncols: int = 80) -> List[str]:
|
|
400
|
"""Wrap multiple paragraphs to fit a specified width.
|
|
423
|
"""Wrap multiple paragraphs to fit a specified width.
|
|
401
|
|
|
424
|
|
|
402
|
This is equivalent to textwrap.wrap, but with support for multiple
|
|
425
|
This is equivalent to textwrap.wrap, but with support for multiple
|
|
@@
-428,7
+451,7
b' def wrap_paragraphs(text, ncols=80):'
|
|
428
|
return out_ps
|
|
451
|
return out_ps
|
|
429
|
|
|
452
|
|
|
430
|
|
|
453
|
|
|
431
|
def strip_email_quotes(text):
|
|
454
|
def strip_email_quotes(text: str) -> str:
|
|
432
|
"""Strip leading email quotation characters ('>').
|
|
455
|
"""Strip leading email quotation characters ('>').
|
|
433
|
|
|
456
|
|
|
434
|
Removes any combination of leading '>' interspersed with whitespace that
|
|
457
|
Removes any combination of leading '>' interspersed with whitespace that
|
|
@@
-478,7
+501,7
b' def strip_email_quotes(text):'
|
|
478
|
return text
|
|
501
|
return text
|
|
479
|
|
|
502
|
|
|
480
|
|
|
503
|
|
|
481
|
def strip_ansi(source):
|
|
504
|
def strip_ansi(source: str) -> str:
|
|
482
|
"""
|
|
505
|
"""
|
|
483
|
Remove ansi escape codes from text.
|
|
506
|
Remove ansi escape codes from text.
|
|
484
|
|
|
507
|
|
|
@@
-519,7
+542,8
b' class EvalFormatter(Formatter):'
|
|
519
|
In [3]: f.format("{greeting[slice(2,4)]}", greeting="Hello")
|
|
542
|
In [3]: f.format("{greeting[slice(2,4)]}", greeting="Hello")
|
|
520
|
Out[3]: 'll'
|
|
543
|
Out[3]: 'll'
|
|
521
|
"""
|
|
544
|
"""
|
|
522
|
def get_field(self, name, args, kwargs):
|
|
545
|
|
|
|
|
|
546
|
def get_field(self, name: str, args: Any, kwargs: Any) -> Tuple[Any, str]:
|
|
523
|
v = eval(name, kwargs)
|
|
547
|
v = eval(name, kwargs)
|
|
524
|
return v, name
|
|
548
|
return v, name
|
|
525
|
|
|
549
|
|
|
@@
-606,11
+630,15
b' class DollarFormatter(FullEvalFormatter):'
|
|
606
|
In [4]: f.format('$a or {b}', a=1, b=2)
|
|
630
|
In [4]: f.format('$a or {b}', a=1, b=2)
|
|
607
|
Out[4]: '1 or 2'
|
|
631
|
Out[4]: '1 or 2'
|
|
608
|
"""
|
|
632
|
"""
|
|
609
|
_dollar_pattern_ignore_single_quote = re.compile(r"(.*?)\$(\$?[\w\.]+)(?=([^']*'[^']*')*[^']*$)")
|
|
633
|
|
|
610
|
def parse(self, fmt_string):
|
|
634
|
_dollar_pattern_ignore_single_quote = re.compile(
|
|
611
|
for literal_txt, field_name, format_spec, conversion \
|
|
635
|
r"(.*?)\$(\$?[\w\.]+)(?=([^']*'[^']*')*[^']*$)"
|
|
612
|
in Formatter.parse(self, fmt_string):
|
|
636
|
)
|
|
613
|
|
|
637
|
|
|
|
|
|
638
|
def parse(self, fmt_string: str) -> Iterator[Tuple[Any, Any, Any, Any]]: # type: ignore
|
|
|
|
|
639
|
for literal_txt, field_name, format_spec, conversion in Formatter.parse(
|
|
|
|
|
640
|
self, fmt_string
|
|
|
|
|
641
|
):
|
|
614
|
# Find $foo patterns in the literal text.
|
|
642
|
# Find $foo patterns in the literal text.
|
|
615
|
continue_from = 0
|
|
643
|
continue_from = 0
|
|
616
|
txt = ""
|
|
644
|
txt = ""
|
|
@@
-627,14
+655,17
b' class DollarFormatter(FullEvalFormatter):'
|
|
627
|
# Re-yield the {foo} style pattern
|
|
655
|
# Re-yield the {foo} style pattern
|
|
628
|
yield (txt + literal_txt[continue_from:], field_name, format_spec, conversion)
|
|
656
|
yield (txt + literal_txt[continue_from:], field_name, format_spec, conversion)
|
|
629
|
|
|
657
|
|
|
630
|
def __repr__(self):
|
|
658
|
def __repr__(self) -> str:
|
|
631
|
return "<DollarFormatter>"
|
|
659
|
return "<DollarFormatter>"
|
|
632
|
|
|
660
|
|
|
633
|
#-----------------------------------------------------------------------------
|
|
661
|
#-----------------------------------------------------------------------------
|
|
634
|
# Utils to columnize a list of string
|
|
662
|
# Utils to columnize a list of string
|
|
635
|
#-----------------------------------------------------------------------------
|
|
663
|
#-----------------------------------------------------------------------------
|
|
636
|
|
|
664
|
|
|
637
|
def _col_chunks(l, max_rows, row_first=False):
|
|
665
|
|
|
|
|
|
666
|
def _col_chunks(
|
|
|
|
|
667
|
l: List[int], max_rows: int, row_first: bool = False
|
|
|
|
|
668
|
) -> Iterator[List[int]]:
|
|
638
|
"""Yield successive max_rows-sized column chunks from l."""
|
|
669
|
"""Yield successive max_rows-sized column chunks from l."""
|
|
639
|
if row_first:
|
|
670
|
if row_first:
|
|
640
|
ncols = (len(l) // max_rows) + (len(l) % max_rows > 0)
|
|
671
|
ncols = (len(l) // max_rows) + (len(l) % max_rows > 0)
|
|
@@
-646,7
+677,7
b' def _col_chunks(l, max_rows, row_first=False):'
|
|
646
|
|
|
677
|
|
|
647
|
|
|
678
|
|
|
648
|
def _find_optimal(
|
|
679
|
def _find_optimal(
|
|
649
|
rlist: List[str], row_first: bool, separator_size: int, displaywidth: int
|
|
680
|
rlist: List[int], row_first: bool, separator_size: int, displaywidth: int
|
|
650
|
) -> Dict[str, Any]:
|
|
681
|
) -> Dict[str, Any]:
|
|
651
|
"""Calculate optimal info to columnize a list of string"""
|
|
682
|
"""Calculate optimal info to columnize a list of string"""
|
|
652
|
for max_rows in range(1, len(rlist) + 1):
|
|
683
|
for max_rows in range(1, len(rlist) + 1):
|
|
@@
-662,7
+693,10
b' def _find_optimal('
|
|
662
|
}
|
|
693
|
}
|
|
663
|
|
|
694
|
|
|
664
|
|
|
695
|
|
|
665
|
def _get_or_default(mylist, i, default=None):
|
|
696
|
T = TypeVar("T")
|
|
|
|
|
697
|
|
|
|
|
|
698
|
|
|
|
|
|
699
|
def _get_or_default(mylist: List[T], i: int, default: T) -> T:
|
|
666
|
"""return list item number, or default if don't exist"""
|
|
700
|
"""return list item number, or default if don't exist"""
|
|
667
|
if i >= len(mylist):
|
|
701
|
if i >= len(mylist):
|
|
668
|
return default
|
|
702
|
return default
|
|
@@
-740,9
+774,31
b' def compute_item_matrix('
|
|
740
|
)
|
|
774
|
)
|
|
741
|
nrow, ncol = info["max_rows"], info["num_columns"]
|
|
775
|
nrow, ncol = info["max_rows"], info["num_columns"]
|
|
742
|
if row_first:
|
|
776
|
if row_first:
|
|
743
|
return ([[_get_or_default(items, r * ncol + c, default=empty) for c in range(ncol)] for r in range(nrow)], info)
|
|
777
|
return (
|
|
|
|
|
778
|
[
|
|
|
|
|
779
|
[
|
|
|
|
|
780
|
_get_or_default(
|
|
|
|
|
781
|
items, r * ncol + c, default=empty
|
|
|
|
|
782
|
) # type:ignore[misc]
|
|
|
|
|
783
|
for c in range(ncol)
|
|
|
|
|
784
|
]
|
|
|
|
|
785
|
for r in range(nrow)
|
|
|
|
|
786
|
],
|
|
|
|
|
787
|
info,
|
|
|
|
|
788
|
)
|
|
744
|
else:
|
|
789
|
else:
|
|
745
|
return ([[_get_or_default(items, c * nrow + r, default=empty) for c in range(ncol)] for r in range(nrow)], info)
|
|
790
|
return (
|
|
|
|
|
791
|
[
|
|
|
|
|
792
|
[
|
|
|
|
|
793
|
_get_or_default(
|
|
|
|
|
794
|
items, c * nrow + r, default=empty
|
|
|
|
|
795
|
) # type:ignore[misc]
|
|
|
|
|
796
|
for c in range(ncol)
|
|
|
|
|
797
|
]
|
|
|
|
|
798
|
for r in range(nrow)
|
|
|
|
|
799
|
],
|
|
|
|
|
800
|
info,
|
|
|
|
|
801
|
)
|
|
746
|
|
|
802
|
|
|
747
|
|
|
803
|
|
|
748
|
def columnize(
|
|
804
|
def columnize(
|
|
@@
-795,7
+851,9
b' def columnize('
|
|
795
|
return "\n".join(map(sjoin, fmatrix)) + "\n"
|
|
851
|
return "\n".join(map(sjoin, fmatrix)) + "\n"
|
|
796
|
|
|
852
|
|
|
797
|
|
|
853
|
|
|
798
|
def get_text_list(list_, last_sep=' and ', sep=", ", wrap_item_with=""):
|
|
854
|
def get_text_list(
|
|
|
|
|
855
|
list_: List[str], last_sep: str = " and ", sep: str = ", ", wrap_item_with: str = ""
|
|
|
|
|
856
|
) -> str:
|
|
799
|
"""
|
|
857
|
"""
|
|
800
|
Return a string with a natural enumeration of items
|
|
858
|
Return a string with a natural enumeration of items
|
|
801
|
|
|
859
|
|