@@ -165,15 +165,15 @@ def split_iter(src, sep=None, maxsplit=None):
165
165
sep_func = sep
166
166
elif not is_scalar (sep ):
167
167
sep = frozenset (sep )
168
- sep_func = lambda x : x in sep
168
+ def sep_func ( x ): return x in sep
169
169
else :
170
- sep_func = lambda x : x == sep
170
+ def sep_func ( x ): return x == sep
171
171
172
172
cur_group = []
173
173
split_count = 0
174
174
for s in src :
175
175
if maxsplit is not None and split_count >= maxsplit :
176
- sep_func = lambda x : False
176
+ def sep_func ( x ): return False
177
177
if sep_func (s ):
178
178
if sep is None and not cur_group :
179
179
# If sep is none, str.split() "groups" separators
@@ -229,7 +229,7 @@ def rstrip(iterable, strip_value=None):
229
229
['Foo', 'Bar']
230
230
231
231
"""
232
- return list (rstrip_iter (iterable ,strip_value ))
232
+ return list (rstrip_iter (iterable , strip_value ))
233
233
234
234
235
235
def rstrip_iter (iterable , strip_value = None ):
@@ -253,7 +253,7 @@ def rstrip_iter(iterable, strip_value=None):
253
253
else :
254
254
broken = True
255
255
break
256
- if not broken : # Return to caller here because the end of the
256
+ if not broken : # Return to caller here because the end of the
257
257
return # iterator has been reached
258
258
yield from cache
259
259
yield i
@@ -268,10 +268,10 @@ def strip(iterable, strip_value=None):
268
268
['Foo', 'Bar', 'Bam']
269
269
270
270
"""
271
- return list (strip_iter (iterable ,strip_value ))
271
+ return list (strip_iter (iterable , strip_value ))
272
272
273
273
274
- def strip_iter (iterable ,strip_value = None ):
274
+ def strip_iter (iterable , strip_value = None ):
275
275
"""Strips values from the beginning and end of an iterable. Stripped items
276
276
will match the value of the argument strip_value. Functionality is
277
277
analogous to that of the method str.strip. Returns a generator.
@@ -280,7 +280,7 @@ def strip_iter(iterable,strip_value=None):
280
280
['Foo', 'Bar', 'Bam']
281
281
282
282
"""
283
- return rstrip_iter (lstrip_iter (iterable ,strip_value ),strip_value )
283
+ return rstrip_iter (lstrip_iter (iterable , strip_value ), strip_value )
284
284
285
285
286
286
def chunked (src , size , count = None , ** kw ):
@@ -340,11 +340,12 @@ def chunked_iter(src, size, **kw):
340
340
raise ValueError ('got unexpected keyword arguments: %r' % kw .keys ())
341
341
if not src :
342
342
return
343
- postprocess = lambda chk : chk
343
+
344
+ def postprocess (chk ): return chk
344
345
if isinstance (src , (str , bytes )):
345
- postprocess = lambda chk , _sep = type (src )(): _sep .join (chk )
346
+ def postprocess ( chk , _sep = type (src )()): return _sep .join (chk )
346
347
if isinstance (src , bytes ):
347
- postprocess = lambda chk : bytes (chk )
348
+ def postprocess ( chk ): return bytes (chk )
348
349
src_iter = iter (src )
349
350
while True :
350
351
cur_chunk = list (itertools .islice (src_iter , size ))
@@ -385,15 +386,19 @@ def chunk_ranges(input_size, chunk_size, input_offset=0, overlap_size=0, align=F
385
386
>>> list(chunk_ranges(input_offset=3, input_size=15, chunk_size=5, overlap_size=1, align=True))
386
387
[(3, 5), (4, 9), (8, 13), (12, 17), (16, 18)]
387
388
"""
388
- input_size = _validate_positive_int (input_size , 'input_size' , strictly_positive = False )
389
+ input_size = _validate_positive_int (
390
+ input_size , 'input_size' , strictly_positive = False )
389
391
chunk_size = _validate_positive_int (chunk_size , 'chunk_size' )
390
- input_offset = _validate_positive_int (input_offset , 'input_offset' , strictly_positive = False )
391
- overlap_size = _validate_positive_int (overlap_size , 'overlap_size' , strictly_positive = False )
392
+ input_offset = _validate_positive_int (
393
+ input_offset , 'input_offset' , strictly_positive = False )
394
+ overlap_size = _validate_positive_int (
395
+ overlap_size , 'overlap_size' , strictly_positive = False )
392
396
393
397
input_stop = input_offset + input_size
394
398
395
399
if align :
396
- initial_chunk_len = chunk_size - input_offset % (chunk_size - overlap_size )
400
+ initial_chunk_len = chunk_size - \
401
+ input_offset % (chunk_size - overlap_size )
397
402
if initial_chunk_len != overlap_size :
398
403
yield (input_offset , min (input_offset + initial_chunk_len , input_stop ))
399
404
if input_offset + initial_chunk_len >= input_stop :
@@ -479,7 +484,7 @@ def windowed_iter(src, size, fill=_UNSET):
479
484
480
485
With *fill* set, the iterator always yields a number of windows
481
486
equal to the length of the *src* iterable.
482
-
487
+
483
488
>>> windowed(range(4), 3, fill=None)
484
489
[(0, 1, 2), (1, 2, 3), (2, 3, None), (3, None, None)]
485
490
@@ -495,17 +500,16 @@ def windowed_iter(src, size, fill=_UNSET):
495
500
except StopIteration :
496
501
return zip ([])
497
502
return zip (* tees )
498
-
503
+
499
504
for i , t in enumerate (tees ):
500
- for _ in range (i ):
505
+ for _ in range (i ):
501
506
try :
502
507
next (t )
503
508
except StopIteration :
504
509
continue
505
510
return zip_longest (* tees , fillvalue = fill )
506
511
507
512
508
-
509
513
def xfrange (stop , start = None , step = 1.0 ):
510
514
"""Same as :func:`frange`, but generator-based instead of returning a
511
515
list.
@@ -726,21 +730,21 @@ def bucketize(src, key=bool, value_transform=None, key_filter=None):
726
730
src = zip (key , src )
727
731
728
732
if isinstance (key , str ):
729
- key_func = lambda x : getattr (x , key , x )
733
+ def key_func ( x ): return getattr (x , key , x )
730
734
elif callable (key ):
731
735
key_func = key
732
736
elif isinstance (key , list ):
733
- key_func = lambda x : x [0 ]
737
+ def key_func ( x ): return x [0 ]
734
738
else :
735
739
raise TypeError ('expected key to be callable or a string or a list' )
736
740
737
741
if value_transform is None :
738
- value_transform = lambda x : x
742
+ def value_transform ( x ): return x
739
743
if not callable (value_transform ):
740
744
raise TypeError ('expected callable value transform function' )
741
745
if isinstance (key , list ):
742
746
f = value_transform
743
- value_transform = lambda x : f (x [1 ])
747
+ def value_transform ( x ): return f (x [1 ])
744
748
745
749
ret = {}
746
750
for val in src :
@@ -807,11 +811,11 @@ def unique_iter(src, key=None):
807
811
if not is_iterable (src ):
808
812
raise TypeError ('expected an iterable, not %r' % type (src ))
809
813
if key is None :
810
- key_func = lambda x : x
814
+ def key_func ( x ): return x
811
815
elif callable (key ):
812
816
key_func = key
813
817
elif isinstance (key , str ):
814
- key_func = lambda x : getattr (x , key , x )
818
+ def key_func ( x ): return getattr (x , key , x )
815
819
else :
816
820
raise TypeError ('"key" expected a string or callable, not %r' % key )
817
821
seen = set ()
@@ -862,7 +866,7 @@ def redundant(src, key=None, groups=False):
862
866
elif callable (key ):
863
867
key_func = key
864
868
elif isinstance (key , (str , bytes )):
865
- key_func = lambda x : getattr (x , key , x )
869
+ def key_func ( x ): return getattr (x , key , x )
866
870
else :
867
871
raise TypeError ('"key" expected a string or callable, not %r' % key )
868
872
seen = {} # key to first seen item
@@ -964,6 +968,7 @@ def flatten_iter(iterable):
964
968
else :
965
969
yield item
966
970
971
+
967
972
def flatten (iterable ):
968
973
"""``flatten()`` returns a collapsed list of all the elements from
969
974
*iterable* while collapsing any nested iterables.
@@ -1006,6 +1011,7 @@ def default_visit(path, key, value):
1006
1011
# print('visit(%r, %r, %r)' % (path, key, value))
1007
1012
return key , value
1008
1013
1014
+
1009
1015
# enable the extreme: monkeypatching iterutils with a different default_visit
1010
1016
_orig_default_visit = default_visit
1011
1017
@@ -1128,6 +1134,9 @@ def remap(root, visit=default_visit, enter=default_enter, exit=default_exit,
1128
1134
callable. When set to ``False``, remap ignores any errors
1129
1135
raised by the *visit* callback. Items causing exceptions
1130
1136
are kept. See examples for more details.
1137
+ trace (bool): Pass ``trace=True`` to print out the entire
1138
+ traversal. Or pass a tuple of ``'visit'``, ``'enter'``,
1139
+ or ``'exit'`` to print only the selected events.
1131
1140
1132
1141
remap is designed to cover the majority of cases with just the
1133
1142
*visit* callable. While passing in multiple callables is very
@@ -1156,6 +1165,15 @@ def remap(root, visit=default_visit, enter=default_enter, exit=default_exit,
1156
1165
if not callable (exit ):
1157
1166
raise TypeError ('exit expected callable, not: %r' % exit )
1158
1167
reraise_visit = kwargs .pop ('reraise_visit' , True )
1168
+ trace = kwargs .pop ('trace' , ())
1169
+ if trace is True :
1170
+ trace = ('visit' , 'enter' , 'exit' )
1171
+ elif isinstance (trace , str ):
1172
+ trace = (trace ,)
1173
+ if not isinstance (trace , (tuple , list , set )):
1174
+ raise TypeError ('trace expected tuple of event names, not: %r' % trace )
1175
+ trace_enter , trace_exit , trace_visit = 'enter' in trace , 'exit' in trace , 'visit' in trace
1176
+
1159
1177
if kwargs :
1160
1178
raise TypeError ('unexpected keyword arguments: %r' % kwargs .keys ())
1161
1179
@@ -1168,14 +1186,23 @@ def remap(root, visit=default_visit, enter=default_enter, exit=default_exit,
1168
1186
key , new_parent , old_parent = value
1169
1187
id_value = id (old_parent )
1170
1188
path , new_items = new_items_stack .pop ()
1189
+ if trace_exit :
1190
+ print (' .. remap exit:' , path , '-' , key , '-' ,
1191
+ old_parent , '-' , new_parent , '-' , new_items )
1171
1192
value = exit (path , key , old_parent , new_parent , new_items )
1193
+ if trace_exit :
1194
+ print (' .. remap exit result:' , value )
1172
1195
registry [id_value ] = value
1173
1196
if not new_items_stack :
1174
1197
continue
1175
1198
elif id_value in registry :
1176
1199
value = registry [id_value ]
1177
1200
else :
1201
+ if trace_enter :
1202
+ print (' .. remap enter:' , path , '-' , key , '-' , value )
1178
1203
res = enter (path , key , value )
1204
+ if trace_enter :
1205
+ print (' .. remap enter result:' , res )
1179
1206
try :
1180
1207
new_parent , new_items = res
1181
1208
except TypeError :
@@ -1191,21 +1218,29 @@ def remap(root, visit=default_visit, enter=default_enter, exit=default_exit,
1191
1218
stack .append ((_REMAP_EXIT , (key , new_parent , value )))
1192
1219
if new_items :
1193
1220
stack .extend (reversed (list (new_items )))
1221
+ if trace_enter :
1222
+ print (' .. remap stack size now:' , len (stack ))
1194
1223
continue
1195
1224
if visit is _orig_default_visit :
1196
1225
# avoid function call overhead by inlining identity operation
1197
1226
visited_item = (key , value )
1198
1227
else :
1199
1228
try :
1229
+ if trace_visit :
1230
+ print (' .. remap visit:' , path , '-' , key , '-' , value )
1200
1231
visited_item = visit (path , key , value )
1201
1232
except Exception :
1202
1233
if reraise_visit :
1203
1234
raise
1204
1235
visited_item = True
1205
1236
if visited_item is False :
1237
+ if trace_visit :
1238
+ print (' .. remap visit result: <drop>' )
1206
1239
continue # drop
1207
1240
elif visited_item is True :
1208
1241
visited_item = (key , value )
1242
+ if trace_visit :
1243
+ print (' .. remap visit result:' , visited_item )
1209
1244
# TODO: typecheck?
1210
1245
# raise TypeError('expected (key, value) from visit(),'
1211
1246
# ' not: %r' % visited_item)
@@ -1221,6 +1256,7 @@ class PathAccessError(KeyError, IndexError, TypeError):
1221
1256
representing what can occur when looking up a path in a nested
1222
1257
object.
1223
1258
"""
1259
+
1224
1260
def __init__ (self , exc , seg , path ):
1225
1261
self .exc = exc
1226
1262
self .seg = seg
@@ -1296,7 +1332,7 @@ def get_path(root, path, default=_UNSET):
1296
1332
return cur
1297
1333
1298
1334
1299
- def research (root , query = lambda p , k , v : True , reraise = False ):
1335
+ def research (root , query = lambda p , k , v : True , reraise = False , enter = default_enter ):
1300
1336
"""The :func:`research` function uses :func:`remap` to recurse over
1301
1337
any data nested in *root*, and find values which match a given
1302
1338
criterion, specified by the *query* callable.
@@ -1343,16 +1379,16 @@ def research(root, query=lambda p, k, v: True, reraise=False):
1343
1379
if not callable (query ):
1344
1380
raise TypeError ('query expected callable, not: %r' % query )
1345
1381
1346
- def enter (path , key , value ):
1382
+ def _enter (path , key , value ):
1347
1383
try :
1348
1384
if query (path , key , value ):
1349
1385
ret .append ((path + (key ,), value ))
1350
1386
except Exception :
1351
1387
if reraise :
1352
1388
raise
1353
- return default_enter (path , key , value )
1389
+ return enter (path , key , value )
1354
1390
1355
- remap (root , enter = enter )
1391
+ remap (root , enter = _enter )
1356
1392
return ret
1357
1393
1358
1394
@@ -1383,6 +1419,7 @@ class GUIDerator:
1383
1419
detect a fork on next iteration and reseed accordingly.
1384
1420
1385
1421
"""
1422
+
1386
1423
def __init__ (self , size = 24 ):
1387
1424
self .size = size
1388
1425
if size < 20 or size > 36 :
@@ -1495,13 +1532,16 @@ def soft_sorted(iterable, first=None, last=None, key=None, reverse=False):
1495
1532
last = last or []
1496
1533
key = key or (lambda x : x )
1497
1534
seq = list (iterable )
1498
- other = [x for x in seq if not ((first and key (x ) in first ) or (last and key (x ) in last ))]
1535
+ other = [x for x in seq if not (
1536
+ (first and key (x ) in first ) or (last and key (x ) in last ))]
1499
1537
other .sort (key = key , reverse = reverse )
1500
1538
1501
1539
if first :
1502
- first = sorted ([x for x in seq if key (x ) in first ], key = lambda x : first .index (key (x )))
1540
+ first = sorted ([x for x in seq if key (x ) in first ],
1541
+ key = lambda x : first .index (key (x )))
1503
1542
if last :
1504
- last = sorted ([x for x in seq if key (x ) in last ], key = lambda x : last .index (key (x )))
1543
+ last = sorted ([x for x in seq if key (x ) in last ],
1544
+ key = lambda x : last .index (key (x )))
1505
1545
return first + other + last
1506
1546
1507
1547
@@ -1536,7 +1576,7 @@ def __lt__(self, other):
1536
1576
ret = obj < other
1537
1577
except TypeError :
1538
1578
ret = ((type (obj ).__name__ , id (type (obj )), obj )
1539
- < (type (other ).__name__ , id (type (other )), other ))
1579
+ < (type (other ).__name__ , id (type (other )), other ))
1540
1580
return ret
1541
1581
1542
1582
if key is not None and not callable (key ):
@@ -1545,6 +1585,7 @@ def __lt__(self, other):
1545
1585
1546
1586
return sorted (iterable , key = _Wrapper , reverse = reverse )
1547
1587
1588
+
1548
1589
"""
1549
1590
May actually be faster to do an isinstance check for a str path
1550
1591
0 commit comments