Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit b9dca8c

Browse files
committedSep 28, 2024·
Replace 'keepends' with 'drop'
Add 'drop' argument in place of 'keepends' for tubes. 'keepends' still works but raises depreciation warning.
1 parent 785ed9f commit b9dca8c

File tree

1 file changed

+92
-42
lines changed

1 file changed

+92
-42
lines changed
 

‎pwnlib/tubes/tube.py

+92-42
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,44 @@ def __init__(self, timeout = default, level = None, *a, **kw):
4444
self._newline = None
4545
atexit.register(self.close)
4646

47+
def _normalize_drop_keepends(self, drop, keepends, drop_default):
48+
'''
49+
>>> t = tube()
50+
>>> t._normalize_drop_keepends(None, None, True)
51+
True
52+
>>> t._normalize_drop_keepends(None, None, False)
53+
False
54+
>>> t._normalize_drop_keepends(True, None, True)
55+
True
56+
>>> t._normalize_drop_keepends(True, None, False)
57+
True
58+
>>> t._normalize_drop_keepends(None, True, True)
59+
False
60+
>>> t._normalize_drop_keepends(None, True, False)
61+
False
62+
>>> t._normalize_drop_keepends(False, None, True)
63+
False
64+
>>> t._normalize_drop_keepends(False, None, False)
65+
False
66+
>>> t._normalize_drop_keepends(None, False, True)
67+
True
68+
>>> t._normalize_drop_keepends(None, False, False)
69+
True
70+
>>> t._normalize_drop_keepends(True, False, False)
71+
Traceback (most recent call last):
72+
...
73+
pwnlib.exception.PwnlibException: 'drop' and 'keepends' arguments cannot be used together.
74+
'''
75+
if keepends is not None:
76+
self.warn_once("'keepends' argument is deprecated. Use 'drop' instead.")
77+
if drop is None and keepends is None:
78+
return drop_default
79+
elif drop is not None:
80+
if keepends is not None:
81+
self.error("'drop' and 'keepends' arguments cannot be used together.")
82+
return drop
83+
return not keepends
84+
4785
@property
4886
def newline(self):
4987
r'''Character sent with methods like sendline() or used for recvline().
@@ -99,7 +137,7 @@ def recv(self, numb = None, timeout = default):
99137
True
100138
>>> with context.local(log_level='debug'):
101139
... _ = t.recv() # doctest: +ELLIPSIS
102-
[...] Received 0xc bytes:
140+
[DEBUG] Received 0xc bytes:
103141
b'Hello, world'
104142
"""
105143
numb = self.buffer.get_fill_size(numb)
@@ -368,8 +406,8 @@ def recvuntil(self, delims, drop=False, timeout=default):
368406

369407
return b''
370408

371-
def recvlines(self, numlines=2**20, keepends=False, timeout=default):
372-
r"""recvlines(numlines, keepends=False, timeout=default) -> list of bytes objects
409+
def recvlines(self, numlines=2**20, drop=None, keepends=None, timeout=default):
410+
r"""recvlines(numlines, drop=True, timeout=default) -> list of bytes objects
373411
374412
Receive up to ``numlines`` lines.
375413
@@ -381,7 +419,7 @@ def recvlines(self, numlines=2**20, keepends=False, timeout=default):
381419
382420
Arguments:
383421
numlines(int): Maximum number of lines to receive
384-
keepends(bool): Keep newlines at the end of each line (:const:`False`).
422+
drop(bool): Drop newlines at the end of each line (:const:`True`).
385423
timeout(int): Maximum timeout
386424
387425
Raises:
@@ -400,17 +438,20 @@ def recvlines(self, numlines=2**20, keepends=False, timeout=default):
400438
>>> t.recv_raw = lambda n: b'Foo\nBar\nBaz\n'
401439
>>> t.recvlines(3)
402440
[b'Foo', b'Bar', b'Baz']
403-
>>> t.recvlines(3, True)
441+
>>> t.recvlines(3, drop=False)
404442
[b'Foo\n', b'Bar\n', b'Baz\n']
405443
"""
444+
drop = self._normalize_drop_keepends(drop, keepends, True)
445+
del keepends
446+
406447
lines = []
407448
with self.countdown(timeout):
408449
for _ in range(numlines):
409450
try:
410-
# We must set 'keepends' to True here so that we can
451+
# We must set 'drop' to False here so that we can
411452
# restore the original, unmodified data to the buffer
412453
# in the event of a timeout.
413-
res = self.recvline(keepends=True, timeout=timeout)
454+
res = self.recvline(drop=False, timeout=timeout)
414455
except Exception:
415456
self.unrecv(b''.join(lines))
416457
raise
@@ -420,13 +461,13 @@ def recvlines(self, numlines=2**20, keepends=False, timeout=default):
420461
else:
421462
break
422463

423-
if not keepends:
424-
lines = [line.rstrip(self.newline) for line in lines]
464+
if drop:
465+
lines = [line[:-len(self.newline)] for line in lines]
425466

426467
return lines
427468

428-
def recvlinesS(self, numlines=2**20, keepends=False, timeout=default):
429-
r"""recvlinesS(numlines, keepends=False, timeout=default) -> str list
469+
def recvlinesS(self, numlines=2**20, drop=None, keepends=None, timeout=default):
470+
r"""recvlinesS(numlines, drop=True, timeout=default) -> str list
430471
431472
This function is identical to :meth:`recvlines`, but decodes
432473
the received bytes into string using :func:`context.encoding`.
@@ -442,10 +483,10 @@ def recvlinesS(self, numlines=2**20, keepends=False, timeout=default):
442483
>>> t.recvlinesS(3)
443484
['Foo', 'Bar', 'Baz']
444485
"""
445-
return [packing._decode(x) for x in self.recvlines(numlines, keepends, timeout)]
486+
return [packing._decode(x) for x in self.recvlines(numlines, drop=drop, keepends=keepends, timeout=timeout)]
446487

447-
def recvlinesb(self, numlines=2**20, keepends=False, timeout=default):
448-
r"""recvlinesb(numlines, keepends=False, timeout=default) -> bytearray list
488+
def recvlinesb(self, numlines=2**20, drop=None, keepends=None, timeout=default):
489+
r"""recvlinesb(numlines, drop=True, timeout=default) -> bytearray list
449490
450491
This function is identical to :meth:`recvlines`, but returns a bytearray.
451492
@@ -459,10 +500,10 @@ def recvlinesb(self, numlines=2**20, keepends=False, timeout=default):
459500
>>> t.recvlinesb(3)
460501
[bytearray(b'Foo'), bytearray(b'Bar'), bytearray(b'Baz')]
461502
"""
462-
return [bytearray(x) for x in self.recvlines(numlines, keepends, timeout)]
503+
return [bytearray(x) for x in self.recvlines(numlines, drop=drop, keepends=keepends, timeout=timeout)]
463504

464-
def recvline(self, keepends=True, timeout=default):
465-
r"""recvline(keepends=True, timeout=default) -> bytes
505+
def recvline(self, drop=None, keepends=None, timeout=default):
506+
r"""recvline(drop=False, timeout=default) -> bytes
466507
467508
Receive a single line from the tube.
468509
@@ -478,7 +519,7 @@ def recvline(self, keepends=True, timeout=default):
478519
all data is buffered and an empty byte string (``b''``) is returned.
479520
480521
Arguments:
481-
keepends(bool): Keep the line ending (:const:`True`).
522+
drop(bool): Drop the line ending (:const:`False`).
482523
timeout(int): Timeout
483524
484525
Raises:
@@ -501,10 +542,10 @@ def recvline(self, keepends=True, timeout=default):
501542
b'Foo\n'
502543
>>> t.recvline()
503544
b'Bar\r\n'
504-
>>> t.recvline(keepends = False)
545+
>>> t.recvline(drop=True)
505546
b'Baz'
506547
>>> t.newline = b'\r\n'
507-
>>> t.recvline(keepends = False)
548+
>>> t.recvline(drop=True)
508549
b'Foo\nBar'
509550
>>> t = tube()
510551
>>> def _recv_eof(n):
@@ -520,20 +561,23 @@ def recvline(self, keepends=True, timeout=default):
520561
b'trailing data'
521562
>>> t.recvline() # doctest: +ELLIPSIS
522563
Traceback (most recent call last):
523-
...
564+
...
524565
EOFError
525566
"""
567+
drop = self._normalize_drop_keepends(drop, keepends, False)
568+
del keepends
569+
526570
try:
527-
return self.recvuntil(self.newline, drop = not keepends, timeout = timeout)
571+
return self.recvuntil(self.newline, drop=drop, timeout=timeout)
528572
except EOFError:
529573
if not context.throw_eof_on_incomplete_line and self.buffer.size > 0:
530574
if context.throw_eof_on_incomplete_line is None:
531575
self.warn_once('EOFError during recvline. Returning buffered data without trailing newline.')
532576
return self.buffer.get()
533577
raise
534578

535-
def recvline_pred(self, pred, keepends=False, timeout=default):
536-
r"""recvline_pred(pred, keepends=False) -> bytes
579+
def recvline_pred(self, pred, drop=None, keepends=None, timeout=default):
580+
r"""recvline_pred(pred, drop=True, timeout=default) -> bytes
537581
538582
Receive data until ``pred(line)`` returns a truthy value.
539583
Drop all other data.
@@ -544,25 +588,28 @@ def recvline_pred(self, pred, keepends=False, timeout=default):
544588
Arguments:
545589
pred(callable): Function to call. Returns the line for which
546590
this function returns :const:`True`.
591+
drop(bool): Drop the line ending (:const:`True`).
547592
548593
Examples:
549594
550595
>>> t = tube()
551596
>>> t.recv_raw = lambda n: b"Foo\nBar\nBaz\n"
552597
>>> t.recvline_pred(lambda line: line == b"Bar\n")
553598
b'Bar'
554-
>>> t.recvline_pred(lambda line: line == b"Bar\n", keepends=True)
599+
>>> t.recvline_pred(lambda line: line == b"Bar\n", drop=False)
555600
b'Bar\n'
556601
>>> t.recvline_pred(lambda line: line == b'Nope!', timeout=0.1)
557602
b''
558603
"""
604+
drop = self._normalize_drop_keepends(drop, keepends, True)
605+
del keepends
559606

560607
tmpbuf = Buffer()
561608
line = b''
562609
with self.countdown(timeout):
563610
while self.countdown_active():
564611
try:
565-
line = self.recvline(keepends=True)
612+
line = self.recvline(drop=False)
566613
except Exception:
567614
self.buffer.unget(tmpbuf)
568615
raise
@@ -572,22 +619,23 @@ def recvline_pred(self, pred, keepends=False, timeout=default):
572619
return b''
573620

574621
if pred(line):
575-
if not keepends:
622+
if drop:
576623
line = line[:-len(self.newline)]
577624
return line
578625
else:
579626
tmpbuf.add(line)
580627

581628
return b''
582629

583-
def recvline_contains(self, items, keepends = False, timeout = default):
584-
r"""
630+
def recvline_contains(self, items, drop=None, keepends=None, timeout=default):
631+
r"""recvline_contains(items, drop=True, timeout=default) -> bytes
632+
585633
Receive lines until one line is found which contains at least
586634
one of `items`.
587635
588636
Arguments:
589637
items(str,tuple): List of strings to search for, or a single string.
590-
keepends(bool): Return lines with newlines if :const:`True`
638+
drop(bool): Drop the line ending (:const:`True`).
591639
timeout(int): Timeout, in seconds
592640
593641
Examples:
@@ -613,10 +661,10 @@ def recvline_contains(self, items, keepends = False, timeout = default):
613661
def pred(line):
614662
return any(d in line for d in items)
615663

616-
return self.recvline_pred(pred, keepends, timeout)
664+
return self.recvline_pred(pred, drop=drop, keepends=keepends, timeout=timeout)
617665

618-
def recvline_startswith(self, delims, keepends=False, timeout=default):
619-
r"""recvline_startswith(delims, keepends=False, timeout=default) -> bytes
666+
def recvline_startswith(self, delims, drop=None, keepends=None, timeout=default):
667+
r"""recvline_startswith(delims, drop=True, timeout=default) -> bytes
620668
621669
Keep receiving lines until one is found that starts with one of
622670
`delims`. Returns the last line received.
@@ -626,7 +674,7 @@ def recvline_startswith(self, delims, keepends=False, timeout=default):
626674
627675
Arguments:
628676
delims(str,tuple): List of strings to search for, or string of single characters
629-
keepends(bool): Return lines with newlines if :const:`True`
677+
drop(bool): Drop the line ending (:const:`True`).
630678
timeout(int): Timeout, in seconds
631679
632680
Returns:
@@ -638,7 +686,7 @@ def recvline_startswith(self, delims, keepends=False, timeout=default):
638686
>>> t.recv_raw = lambda n: b"Hello\nWorld\nXylophone\n"
639687
>>> t.recvline_startswith((b'W',b'X',b'Y',b'Z'))
640688
b'World'
641-
>>> t.recvline_startswith((b'W',b'X',b'Y',b'Z'), True)
689+
>>> t.recvline_startswith((b'W',b'X',b'Y',b'Z'), drop=False)
642690
b'Xylophone\n'
643691
>>> t.recvline_startswith(b'Wo')
644692
b'World'
@@ -649,11 +697,12 @@ def recvline_startswith(self, delims, keepends=False, timeout=default):
649697
delims = tuple(map(packing._need_bytes, delims))
650698

651699
return self.recvline_pred(lambda line: any(map(line.startswith, delims)),
700+
drop=drop,
652701
keepends=keepends,
653702
timeout=timeout)
654703

655-
def recvline_endswith(self, delims, keepends=False, timeout=default):
656-
r"""recvline_endswith(delims, keepends=False, timeout=default) -> bytes
704+
def recvline_endswith(self, delims, drop=None, keepends=None, timeout=default):
705+
r"""recvline_endswith(delims, drop=True, timeout=default) -> bytes
657706
658707
Keep receiving lines until one is found that ends with one of
659708
`delims`. Returns the last line received.
@@ -669,7 +718,7 @@ def recvline_endswith(self, delims, keepends=False, timeout=default):
669718
>>> t.recv_raw = lambda n: b'Foo\nBar\nBaz\nKaboodle\n'
670719
>>> t.recvline_endswith(b'r')
671720
b'Bar'
672-
>>> t.recvline_endswith((b'a',b'b',b'c',b'd',b'e'), True)
721+
>>> t.recvline_endswith((b'a',b'b',b'c',b'd',b'e'), drop=False)
673722
b'Kaboodle\n'
674723
>>> t.recvline_endswith(b'oodle')
675724
b'Kaboodle'
@@ -681,6 +730,7 @@ def recvline_endswith(self, delims, keepends=False, timeout=default):
681730
delims = tuple(packing._need_bytes(delim) + self.newline for delim in delims)
682731

683732
return self.recvline_pred(lambda line: any(map(line.endswith, delims)),
733+
drop=drop,
684734
keepends=keepends,
685735
timeout=timeout)
686736

@@ -724,8 +774,8 @@ def recvregex(self, regex, exact=False, timeout=default, capture=False):
724774
else:
725775
return self.recvpred(pred, timeout = timeout)
726776

727-
def recvline_regex(self, regex, exact=False, keepends=False, timeout=default):
728-
"""recvline_regex(regex, exact=False, keepends=False, timeout=default) -> bytes
777+
def recvline_regex(self, regex, exact=False, drop=None, keepends=None, timeout=default):
778+
"""recvline_regex(regex, exact=False, drop=True, timeout=default) -> bytes
729779
730780
Wrapper around :func:`recvline_pred`, which will return when a regex
731781
matches a line.
@@ -746,7 +796,7 @@ def recvline_regex(self, regex, exact=False, keepends=False, timeout=default):
746796
else:
747797
pred = regex.search
748798

749-
return self.recvline_pred(pred, keepends = keepends, timeout = timeout)
799+
return self.recvline_pred(pred, drop=drop, keepends=keepends, timeout=timeout)
750800

751801
def recvrepeat(self, timeout=default):
752802
"""recvrepeat(timeout=default) -> bytes
@@ -1062,7 +1112,7 @@ def clean_and_log(self, timeout = 0.05):
10621112
>>> t.connected_raw = lambda d: True
10631113
>>> t.fileno = lambda: 1234
10641114
>>> with context.local(log_level='info'):
1065-
... data = t.clean_and_log() #doctest: +ELLIPSIS
1115+
... data = t.clean_and_log()
10661116
[DEBUG] Received 0xb bytes:
10671117
b'hooray_data'
10681118
>>> data

0 commit comments

Comments
 (0)
Please sign in to comment.