Skip to content

Commit fa0f8bb

Browse files
committed
add explain feature
1 parent fbfb5f0 commit fa0f8bb

File tree

3 files changed

+216
-31
lines changed

3 files changed

+216
-31
lines changed

setup.cfg

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,3 +16,6 @@ exclude_lines =
1616
source =
1717
src
1818
.tox/*/site-packages
19+
20+
[flake8]
21+
max-line-length = 120

src/hyperframe/frame.py

Lines changed: 105 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -83,19 +83,37 @@ def __init__(self, stream_id, flags=()):
8383
)
8484

8585
def __repr__(self):
86-
flags = ", ".join(self.flags) or "None"
87-
body = binascii.hexlify(self.serialize_body()).decode('ascii')
88-
if len(body) > 20:
89-
body = body[:20] + "..."
9086
return (
91-
"{type}(Stream: {stream}; Flags: {flags}): {body}"
87+
"{}(stream_id: {}; flags: {}): {}"
9288
).format(
93-
type=type(self).__name__,
94-
stream=self.stream_id,
95-
flags=flags,
96-
body=body,
89+
type(self).__name__,
90+
self.stream_id,
91+
", ".join(sorted(self.flags)) or "None",
92+
self._body_repr(),
9793
)
9894

95+
def _body_repr(self):
96+
# More specific implementation may be provided by subclasses of Frame.
97+
# This fallback shows the serialized (and truncated) body content.
98+
return _raw_data_repr(self.serialize_body())
99+
100+
@staticmethod
101+
def explain(data):
102+
"""
103+
Takes a bytestring and tries to parse a single frame and print it.
104+
105+
This function is only provided for debugging purposes.
106+
107+
:param data: A memoryview object containing the raw data of at least
108+
one complete frame (header and body).
109+
110+
.. versionadded:: 6.0.0
111+
"""
112+
frame, length = Frame.parse_frame_header(data[:9])
113+
frame.parse_body(data[9:9 + length])
114+
print(frame)
115+
return frame, length
116+
99117
@staticmethod
100118
def parse_frame_header(header, strict=False):
101119
"""
@@ -331,6 +349,13 @@ class PriorityFrame(Priority, Frame):
331349

332350
stream_association = _STREAM_ASSOC_HAS_STREAM
333351

352+
def _body_repr(self):
353+
return "exclusive: {}, depends_on: {}, stream_weight: {}".format(
354+
self.exclusive,
355+
self.depends_on,
356+
self.stream_weight
357+
)
358+
334359
def serialize_body(self):
335360
return self.serialize_priority_data()
336361

@@ -368,6 +393,11 @@ def __init__(self, stream_id, error_code=0, **kwargs):
368393
#: The error code used when resetting the stream.
369394
self.error_code = error_code
370395

396+
def _body_repr(self):
397+
return "error_code: {}".format(
398+
self.error_code,
399+
)
400+
371401
def serialize_body(self):
372402
return _STRUCT_L.pack(self.error_code)
373403

@@ -434,6 +464,11 @@ def __init__(self, stream_id=0, settings=None, **kwargs):
434464
#: A dictionary of the setting type byte to the value of the setting.
435465
self.settings = settings or {}
436466

467+
def _body_repr(self):
468+
return "settings: {}".format(
469+
self.settings,
470+
)
471+
437472
def serialize_body(self):
438473
return b''.join([_STRUCT_HL.pack(setting & 0xFF, value)
439474
for setting, value in self.settings.items()])
@@ -484,6 +519,12 @@ def __init__(self, stream_id, promised_stream_id=0, data=b'', **kwargs):
484519
#: stream.
485520
self.data = data
486521

522+
def _body_repr(self):
523+
return "promised_stream_id: {}, data: {}".format(
524+
self.promised_stream_id,
525+
_raw_data_repr(self.data),
526+
)
527+
487528
def serialize_body(self):
488529
padding_data = self.serialize_padding_data()
489530
padding = b'\0' * self.pad_length
@@ -535,6 +576,11 @@ def __init__(self, stream_id=0, opaque_data=b'', **kwargs):
535576
#: The opaque data sent in this PING frame, as a bytestring.
536577
self.opaque_data = opaque_data
537578

579+
def _body_repr(self):
580+
return "opaque_data: {}".format(
581+
self.opaque_data,
582+
)
583+
538584
def serialize_body(self):
539585
if len(self.opaque_data) > 8:
540586
raise InvalidFrameError(
@@ -588,6 +634,13 @@ def __init__(self,
588634
#: Any additional data sent in the GOAWAY.
589635
self.additional_data = additional_data
590636

637+
def _body_repr(self):
638+
return "last_stream_id: {}, error_code: {}, additional_data: {}".format(
639+
self.last_stream_id,
640+
self.error_code,
641+
self.additional_data,
642+
)
643+
591644
def serialize_body(self):
592645
data = _STRUCT_LL.pack(
593646
self.last_stream_id & 0x7FFFFFFF,
@@ -638,6 +691,11 @@ def __init__(self, stream_id, window_increment=0, **kwargs):
638691
#: The amount the flow control window is to be incremented.
639692
self.window_increment = window_increment
640693

694+
def _body_repr(self):
695+
return "window_increment: {}".format(
696+
self.window_increment,
697+
)
698+
641699
def serialize_body(self):
642700
return _STRUCT_L.pack(self.window_increment & 0x7FFFFFFF)
643701

@@ -692,6 +750,14 @@ def __init__(self, stream_id, data=b'', **kwargs):
692750
#: The HPACK-encoded header block.
693751
self.data = data
694752

753+
def _body_repr(self):
754+
return "exclusive: {}, depends_on: {}, stream_weight: {}, data: {}".format(
755+
self.exclusive,
756+
self.depends_on,
757+
self.stream_weight,
758+
_raw_data_repr(self.data),
759+
)
760+
695761
def serialize_body(self):
696762
padding_data = self.serialize_padding_data()
697763
padding = b'\0' * self.pad_length
@@ -745,6 +811,11 @@ def __init__(self, stream_id, data=b'', **kwargs):
745811
#: The HPACK-encoded header block.
746812
self.data = data
747813

814+
def _body_repr(self):
815+
return "data: {}".format(
816+
_raw_data_repr(self.data),
817+
)
818+
748819
def serialize_body(self):
749820
return self.data
750821

@@ -782,6 +853,12 @@ def __init__(self, stream_id, origin=b'', field=b'', **kwargs):
782853
self.origin = origin
783854
self.field = field
784855

856+
def _body_repr(self):
857+
return "origin: {}, field: {}".format(
858+
self.origin,
859+
self.field,
860+
)
861+
785862
def serialize_body(self):
786863
origin_len = _STRUCT_H.pack(len(self.origin))
787864
return b''.join([origin_len, self.origin, self.field])
@@ -819,10 +896,18 @@ class ExtensionFrame(Frame):
819896

820897
stream_association = _STREAM_ASSOC_EITHER
821898

822-
def __init__(self, type, stream_id, **kwargs):
899+
def __init__(self, type, stream_id, flag_byte=0x0, body=b'', **kwargs):
823900
super().__init__(stream_id, **kwargs)
824901
self.type = type
825-
self.flag_byte = None
902+
self.flag_byte = flag_byte
903+
self.body = body
904+
905+
def _body_repr(self):
906+
return "type: {}, flag_byte: {}, body: {}".format(
907+
self.type,
908+
self.flag_byte,
909+
_raw_data_repr(self.body),
910+
)
826911

827912
def parse_flags(self, flag_byte):
828913
"""
@@ -856,6 +941,15 @@ def serialize(self):
856941
return header + self.body
857942

858943

944+
def _raw_data_repr(data):
945+
if not data:
946+
return "None"
947+
r = binascii.hexlify(data).decode('ascii')
948+
if len(r) > 20:
949+
r = r[:20] + "..."
950+
return r
951+
952+
859953
_FRAME_CLASSES = [
860954
DataFrame,
861955
HeadersFrame,

0 commit comments

Comments
 (0)