• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

Gallopsled / pwntools / 26245642219

21 May 2026 01:59PM UTC coverage: 73.939% (+0.01%) from 73.928%
26245642219

push

github

web-flow
Fix atexception handlers in term mode (#2730)

* Fix atexception handlers in term mode

The colored-traceback module registers its own `sys.excepthook` but doesn't call the original handler.
Reorder the includes such that the colored-traceback is setup before and our own hooks afterwards.
We always call the old excepthook and forward the exception to the colored-traceback after firing our own handlers.

It worked before in non-term mode when stderr wasn't a tty and colors were disabled.

* Update CHANGELOG

* Import atexception in doctests

3941 of 6616 branches covered (59.57%)

4 of 8 new or added lines in 1 file covered. (50.0%)

5 existing lines in 3 files now uncovered.

13593 of 18384 relevant lines covered (73.94%)

0.74 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

89.83
/pwnlib/tubes/buffer.py
1
from pwnlib.context import context
1✔
2

3

4
class Buffer:
1✔
5
    """
6
    List of strings with some helper routines.
7

8
    Example:
9

10
        >>> b = Buffer()
11
        >>> b.add(b"A" * 10)
12
        >>> b.add(b"B" * 10)
13
        >>> len(b)
14
        20
15
        >>> b.get(1)
16
        b'A'
17
        >>> len(b)
18
        19
19
        >>> b.get(9999)
20
        b'AAAAAAAAABBBBBBBBBB'
21
        >>> len(b)
22
        0
23
        >>> b.get(1)
24
        b''
25

26
    Implementation Details:
27

28
        Implemented as a list.  Strings are added onto the end.
29
        The ``0th`` item in the buffer is the oldest item, and
30
        will be received first.
31
    """
32
    def __init__(self, buffer_fill_size = None):
1✔
33
        self.data = [] # Buffer
1✔
34
        self.size = 0  # Length
1✔
35
        self.buffer_fill_size = buffer_fill_size
1✔
36

37
    def __len__(self):
1✔
38
        """
39
        >>> b = Buffer()
40
        >>> b.add(b'lol')
41
        >>> len(b) == 3
42
        True
43
        >>> b.add(b'foobar')
44
        >>> len(b) == 9
45
        True
46
        """
47
        return self.size
1✔
48

49
    def __nonzero__(self):
1✔
50
        return len(self) > 0
×
51

52
    def __contains__(self, x):
1✔
53
        """
54
        >>> b = Buffer()
55
        >>> b.add(b'asdf')
56
        >>> b'x' in b
57
        False
58
        >>> b.add(b'x')
59
        >>> b'x' in b
60
        True
61
        """
62
        for b in self.data:
1✔
63
            if x in b:
1✔
64
                return True
1✔
65
        return False
1✔
66

67
    def index(self, x):
1✔
68
        """
69
        >>> b = Buffer()
70
        >>> b.add(b'asdf')
71
        >>> b.add(b'qwert')
72
        >>> b.index(b't') == len(b) - 1
73
        True
74
        """
75
        sofar = 0
1✔
76
        for b in self.data:
1!
77
            if x in b:
1✔
78
                return sofar + b.index(x)
1✔
79
            sofar += len(b)
1✔
80
        raise IndexError()
×
81

82
    def add(self, data):
1✔
83
        """
84
        Adds data to the buffer.
85

86
        Arguments:
87
            data(str,Buffer): Data to add
88
        """
89
        # Fast path for ''
90
        if not data: return
1!
91

92
        if isinstance(data, Buffer):
1!
93
            self.size += data.size
×
94
            self.data += data.data
×
95
        else:
96
            self.size += len(data)
1✔
97
            self.data.append(data)
1✔
98

99
    def unget(self, data):
1✔
100
        """
101
        Places data at the front of the buffer.
102

103
        Arguments:
104
            data(str,Buffer): Data to place at the beginning of the buffer.
105

106
        Example:
107

108
            >>> b = Buffer()
109
            >>> b.add(b"hello")
110
            >>> b.add(b"world")
111
            >>> b.get(5)
112
            b'hello'
113
            >>> b.unget(b"goodbye")
114
            >>> b.get()
115
            b'goodbyeworld'
116
        """
117
        if isinstance(data, Buffer):
1!
UNCOV
118
            self.data = data.data + self.data
×
UNCOV
119
            self.size += data.size
×
120
        else:
121
            self.data.insert(0, data)
1✔
122
            self.size += len(data)
1✔
123

124
    def get(self, want=float('inf')):
1✔
125
        """
126
        Retrieves bytes from the buffer.
127

128
        Arguments:
129
            want(int): Maximum number of bytes to fetch
130

131
        Returns:
132
            Data as string
133

134
        Example:
135

136
            >>> b = Buffer()
137
            >>> b.add(b'hello')
138
            >>> b.add(b'world')
139
            >>> b.get(1)
140
            b'h'
141
            >>> b.get()
142
            b'elloworld'
143
        """
144
        # Fast path, get all of the data
145
        if want >= self.size:
1✔
146
            data   = b''.join(self.data)
1✔
147
            self.size = 0
1✔
148
            self.data = []
1✔
149
            return data
1✔
150

151
        # Slow path, find the correct-index chunk
152
        have = 0
1✔
153
        i    = 0
1✔
154
        while want >= have:
1✔
155
            have += len(self.data[i])
1✔
156
            i    += 1
1✔
157

158
        # Join the chunks, evict from the buffer
159
        data   = b''.join(self.data[:i])
1✔
160
        self.data = self.data[i:]
1✔
161

162
        # If the last chunk puts us over the limit,
163
        # stick the extra back at the beginning.
164
        if have > want:
1!
165
            extra = data[want:]
1✔
166
            data  = data[:want]
1✔
167
            self.data.insert(0, extra)
1✔
168

169
        # Size update
170
        self.size -= len(data)
1✔
171

172
        return data
1✔
173

174
    def get_fill_size(self, size=None):
1✔
175
        """
176
        Retrieves the default fill size for this buffer class.
177

178
        Arguments:
179
            size (int): (Optional) If set and not None, returns the size variable back.
180

181
        Returns:
182
            Fill size as integer if size is None, else size.
183
        """
184
        if size is None:
1✔
185
            size = self.buffer_fill_size
1✔
186

187
        with context.local(buffer_size=size):
1✔
188
            return context.buffer_size
1✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc