Skip to content

Commit 131b44c

Browse files
committed
add typing information
1 parent 3789435 commit 131b44c

20 files changed

+94
-101
lines changed

setup.cfg

+1
Original file line numberDiff line numberDiff line change
@@ -18,5 +18,6 @@ source =
1818

1919
[flake8]
2020
max-complexity = 10
21+
max-line-length = 120
2122
exclude =
2223
hpack/huffman_constants.py

setup.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@
2929
author_email='[email protected]',
3030
url='https://github.com/python-hyper/hpack',
3131
packages=find_packages(where="src"),
32-
package_data={'hpack': []},
32+
package_data={'hpack': ['py.typed']},
3333
package_dir={'': 'src'},
3434
python_requires='>=3.9.0',
3535
license='MIT License',

src/hpack/__init__.py

-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
# -*- coding: utf-8 -*-
21
"""
32
hpack
43
~~~~~

src/hpack/exceptions.py

-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
# -*- coding: utf-8 -*-
21
"""
32
hyper/http20/exceptions
43
~~~~~~~~~~~~~~~~~~~~~~~

src/hpack/hpack.py

+48-50
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
1-
# -*- coding: utf-8 -*-
21
"""
32
hpack/hpack
43
~~~~~~~~~~~
54
65
Implements the HPACK header compression algorithm as detailed by the IETF.
76
"""
87
import logging
8+
from typing import Any, Generator, Union
99

1010
from .table import HeaderTable, table_entry_size
1111
from .exceptions import (
@@ -16,7 +16,7 @@
1616
REQUEST_CODES, REQUEST_CODES_LENGTH
1717
)
1818
from .huffman_table import decode_huffman
19-
from .struct import HeaderTuple, NeverIndexedHeaderTuple
19+
from .struct import HeaderTuple, NeverIndexedHeaderTuple, Headers
2020

2121
log = logging.getLogger(__name__)
2222

@@ -29,31 +29,25 @@
2929
# as prefix numbers are not zero indexed.
3030
_PREFIX_BIT_MAX_NUMBERS = [(2 ** i) - 1 for i in range(9)]
3131

32-
try: # pragma: no cover
33-
basestring = basestring
34-
except NameError: # pragma: no cover
35-
basestring = (str, bytes)
36-
37-
3832
# We default the maximum header list we're willing to accept to 64kB. That's a
3933
# lot of headers, but if applications want to raise it they can do.
4034
DEFAULT_MAX_HEADER_LIST_SIZE = 2 ** 16
4135

4236

43-
def _unicode_if_needed(header, raw):
37+
def _unicode_if_needed(header: HeaderTuple, raw: bool) -> HeaderTuple:
4438
"""
4539
Provides a header as a unicode string if raw is False, otherwise returns
4640
it as a bytestring.
4741
"""
48-
name = bytes(header[0])
49-
value = bytes(header[1])
42+
name = bytes(header[0]) # type: ignore
43+
value = bytes(header[1]) # type: ignore
5044
if not raw:
51-
name = name.decode('utf-8')
52-
value = value.decode('utf-8')
53-
return header.__class__(name, value)
45+
return header.__class__(name.decode('utf-8'), value.decode('utf-8'))
46+
else:
47+
return header.__class__(name, value)
5448

5549

56-
def encode_integer(integer, prefix_bits):
50+
def encode_integer(integer: int, prefix_bits: int) -> bytearray:
5751
"""
5852
This encodes an integer according to the wacky integer encoding rules
5953
defined in the HPACK spec.
@@ -87,7 +81,7 @@ def encode_integer(integer, prefix_bits):
8781
return bytearray(elements)
8882

8983

90-
def decode_integer(data, prefix_bits):
84+
def decode_integer(data: bytes, prefix_bits: int) -> tuple[int, int]:
9185
"""
9286
This decodes an integer according to the wacky integer encoding rules
9387
defined in the HPACK spec. Returns a tuple of the decoded integer and the
@@ -128,7 +122,8 @@ def decode_integer(data, prefix_bits):
128122
return number, index
129123

130124

131-
def _dict_to_iterable(header_dict):
125+
def _dict_to_iterable(header_dict: Union[dict[bytes, bytes], dict[str, str]]) \
126+
-> Generator[Union[tuple[bytes, bytes], tuple[str, str]], None, None]:
132127
"""
133128
This converts a dictionary to an iterable of two-tuples. This is a
134129
HPACK-specific function because it pulls "special-headers" out first and
@@ -140,19 +135,19 @@ def _dict_to_iterable(header_dict):
140135
key=lambda k: not _to_bytes(k).startswith(b':')
141136
)
142137
for key in keys:
143-
yield key, header_dict[key]
138+
yield key, header_dict[key] # type: ignore
144139

145140

146-
def _to_bytes(value):
141+
def _to_bytes(value: Union[bytes, str, Any]) -> bytes:
147142
"""
148143
Convert anything to bytes through a UTF-8 encoded string
149144
"""
150145
t = type(value)
151146
if t is bytes:
152-
return value
147+
return value # type: ignore
153148
if t is not str:
154149
value = str(value)
155-
return value.encode("utf-8")
150+
return value.encode("utf-8") # type: ignore
156151

157152

158153
class Encoder:
@@ -161,27 +156,29 @@ class Encoder:
161156
HTTP/2 header blocks.
162157
"""
163158

164-
def __init__(self):
159+
def __init__(self) -> None:
165160
self.header_table = HeaderTable()
166161
self.huffman_coder = HuffmanEncoder(
167162
REQUEST_CODES, REQUEST_CODES_LENGTH
168163
)
169-
self.table_size_changes = []
164+
self.table_size_changes: list[int] = []
170165

171166
@property
172-
def header_table_size(self):
167+
def header_table_size(self) -> int:
173168
"""
174169
Controls the size of the HPACK header table.
175170
"""
176171
return self.header_table.maxsize
177172

178173
@header_table_size.setter
179-
def header_table_size(self, value):
174+
def header_table_size(self, value: int) -> None:
180175
self.header_table.maxsize = value
181176
if self.header_table.resized:
182177
self.table_size_changes.append(value)
183178

184-
def encode(self, headers, huffman=True):
179+
def encode(self,
180+
headers: Headers,
181+
huffman: bool = True) -> bytes:
185182
"""
186183
Takes a set of headers and encodes them into a HPACK-encoded header
187184
block.
@@ -256,13 +253,13 @@ def encode(self, headers, huffman=True):
256253
header = (_to_bytes(header[0]), _to_bytes(header[1]))
257254
header_block.append(self.add(header, sensitive, huffman))
258255

259-
header_block = b''.join(header_block)
256+
encoded = b''.join(header_block)
260257

261-
log.debug("Encoded header block to %s", header_block)
258+
log.debug("Encoded header block to %s", encoded)
262259

263-
return header_block
260+
return encoded
264261

265-
def add(self, to_add, sensitive, huffman=False):
262+
def add(self, to_add: tuple[bytes, bytes], sensitive: bool, huffman: bool = False) -> bytes:
266263
"""
267264
This function takes a header key-value tuple and serializes it.
268265
"""
@@ -311,15 +308,15 @@ def add(self, to_add, sensitive, huffman=False):
311308

312309
return encoded
313310

314-
def _encode_indexed(self, index):
311+
def _encode_indexed(self, index: int) -> bytes:
315312
"""
316313
Encodes a header using the indexed representation.
317314
"""
318315
field = encode_integer(index, 7)
319316
field[0] |= 0x80 # we set the top bit
320317
return bytes(field)
321318

322-
def _encode_literal(self, name, value, indexbit, huffman=False):
319+
def _encode_literal(self, name: bytes, value: bytes, indexbit: bytes, huffman: bool = False) -> bytes:
323320
"""
324321
Encodes a header with a literal name and literal value. If ``indexing``
325322
is True, the header will be added to the header table: otherwise it
@@ -340,7 +337,7 @@ def _encode_literal(self, name, value, indexbit, huffman=False):
340337
[indexbit, bytes(name_len), name, bytes(value_len), value]
341338
)
342339

343-
def _encode_indexed_literal(self, index, value, indexbit, huffman=False):
340+
def _encode_indexed_literal(self, index: int, value: bytes, indexbit: bytes, huffman: bool = False) -> bytes:
344341
"""
345342
Encodes a header with an indexed name and a literal value and performs
346343
incremental indexing.
@@ -362,16 +359,16 @@ def _encode_indexed_literal(self, index, value, indexbit, huffman=False):
362359

363360
return b''.join([bytes(prefix), bytes(value_len), value])
364361

365-
def _encode_table_size_change(self):
362+
def _encode_table_size_change(self) -> bytes:
366363
"""
367364
Produces the encoded form of all header table size change context
368365
updates.
369366
"""
370367
block = b''
371368
for size_bytes in self.table_size_changes:
372-
size_bytes = encode_integer(size_bytes, 5)
373-
size_bytes[0] |= 0x20
374-
block += bytes(size_bytes)
369+
b = encode_integer(size_bytes, 5)
370+
b[0] |= 0x20
371+
block += bytes(b)
375372
self.table_size_changes = []
376373
return block
377374

@@ -397,7 +394,7 @@ class Decoder:
397394
Defaults to 64kB.
398395
:type max_header_list_size: ``int``
399396
"""
400-
def __init__(self, max_header_list_size=DEFAULT_MAX_HEADER_LIST_SIZE):
397+
def __init__(self, max_header_list_size: int = DEFAULT_MAX_HEADER_LIST_SIZE) -> None:
401398
self.header_table = HeaderTable()
402399

403400
#: The maximum decompressed size we will allow for any single header
@@ -426,17 +423,17 @@ def __init__(self, max_header_list_size=DEFAULT_MAX_HEADER_LIST_SIZE):
426423
self.max_allowed_table_size = self.header_table.maxsize
427424

428425
@property
429-
def header_table_size(self):
426+
def header_table_size(self) -> int:
430427
"""
431428
Controls the size of the HPACK header table.
432429
"""
433430
return self.header_table.maxsize
434431

435432
@header_table_size.setter
436-
def header_table_size(self, value):
433+
def header_table_size(self, value: int) -> None:
437434
self.header_table.maxsize = value
438435

439-
def decode(self, data, raw=False):
436+
def decode(self, data: bytes, raw: bool = False) -> Headers:
440437
"""
441438
Takes an HPACK-encoded header block and decodes it into a header set.
442439
@@ -454,7 +451,7 @@ def decode(self, data, raw=False):
454451
log.debug("Decoding %s", data)
455452

456453
data_mem = memoryview(data)
457-
headers = []
454+
headers: list[HeaderTuple] = []
458455
data_len = len(data)
459456
inflated_size = 0
460457
current_index = 0
@@ -501,7 +498,7 @@ def decode(self, data, raw=False):
501498

502499
if header:
503500
headers.append(header)
504-
inflated_size += table_entry_size(*header)
501+
inflated_size += table_entry_size(header[0], header[1])
505502

506503
if inflated_size > self.max_header_list_size:
507504
raise OversizedHeaderListError(
@@ -521,7 +518,7 @@ def decode(self, data, raw=False):
521518
except UnicodeDecodeError:
522519
raise HPACKDecodingError("Unable to decode headers as UTF-8.")
523520

524-
def _assert_valid_table_size(self):
521+
def _assert_valid_table_size(self) -> None:
525522
"""
526523
Check that the table size set by the encoder is lower than the maximum
527524
we expect to have.
@@ -531,7 +528,7 @@ def _assert_valid_table_size(self):
531528
"Encoder did not shrink table size to within the max"
532529
)
533530

534-
def _update_encoding_context(self, data):
531+
def _update_encoding_context(self, data: bytes) -> int:
535532
"""
536533
Handles a byte that updates the encoding context.
537534
"""
@@ -544,7 +541,7 @@ def _update_encoding_context(self, data):
544541
self.header_table_size = new_size
545542
return consumed
546543

547-
def _decode_indexed(self, data):
544+
def _decode_indexed(self, data: bytes) -> tuple[HeaderTuple, int]:
548545
"""
549546
Decodes a header represented using the indexed representation.
550547
"""
@@ -553,13 +550,13 @@ def _decode_indexed(self, data):
553550
log.debug("Decoded %s, consumed %d", header, consumed)
554551
return header, consumed
555552

556-
def _decode_literal_no_index(self, data):
553+
def _decode_literal_no_index(self, data: bytes) -> tuple[HeaderTuple, int]:
557554
return self._decode_literal(data, False)
558555

559-
def _decode_literal_index(self, data):
556+
def _decode_literal_index(self, data: bytes) -> tuple[HeaderTuple, int]:
560557
return self._decode_literal(data, True)
561558

562-
def _decode_literal(self, data, should_index):
559+
def _decode_literal(self, data: bytes, should_index: bool) -> tuple[HeaderTuple, int]:
563560
"""
564561
Decodes a header represented with a literal.
565562
"""
@@ -577,7 +574,7 @@ def _decode_literal(self, data, should_index):
577574
high_byte = data[0]
578575
indexed_name = high_byte & 0x0F
579576
name_len = 4
580-
not_indexable = high_byte & 0x10
577+
not_indexable = bool(high_byte & 0x10)
581578

582579
if indexed_name:
583580
# Indexed header name.
@@ -616,6 +613,7 @@ def _decode_literal(self, data, should_index):
616613

617614
# If we have been told never to index the header field, encode that in
618615
# the tuple we use.
616+
header: HeaderTuple
619617
if not_indexable:
620618
header = NeverIndexedHeaderTuple(name, value)
621619
else:

src/hpack/huffman.py

+8-9
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
# -*- coding: utf-8 -*-
21
"""
32
hpack/huffman_decoder
43
~~~~~~~~~~~~~~~~~~~~~
@@ -13,11 +12,11 @@ class HuffmanEncoder:
1312
Encodes a string according to the Huffman encoding table defined in the
1413
HPACK specification.
1514
"""
16-
def __init__(self, huffman_code_list, huffman_code_list_lengths):
15+
def __init__(self, huffman_code_list: list[int], huffman_code_list_lengths: list[int]) -> None:
1716
self.huffman_code_list = huffman_code_list
1817
self.huffman_code_list_lengths = huffman_code_list_lengths
1918

20-
def encode(self, bytes_to_encode):
19+
def encode(self, bytes_to_encode: bytes) -> bytes:
2120
"""
2221
Given a string of bytes, encodes them according to the HPACK Huffman
2322
specification.
@@ -48,19 +47,19 @@ def encode(self, bytes_to_encode):
4847

4948
# Convert the number to hex and strip off the leading '0x' and the
5049
# trailing 'L', if present.
51-
final_num = hex(final_num)[2:].rstrip('L')
50+
s = hex(final_num)[2:].rstrip('L')
5251

5352
# If this is odd, prepend a zero.
54-
final_num = '0' + final_num if len(final_num) % 2 != 0 else final_num
53+
s = '0' + s if len(s) % 2 != 0 else s
5554

5655
# This number should have twice as many digits as bytes. If not, we're
5756
# missing some leading zeroes. Work out how many bytes we want and how
5857
# many digits we have, then add the missing zero digits to the front.
5958
total_bytes = (final_int_len + bits_to_be_padded) // 8
6059
expected_digits = total_bytes * 2
6160

62-
if len(final_num) != expected_digits:
63-
missing_digits = expected_digits - len(final_num)
64-
final_num = ('0' * missing_digits) + final_num
61+
if len(s) != expected_digits:
62+
missing_digits = expected_digits - len(s)
63+
s = ('0' * missing_digits) + s
6564

66-
return bytes.fromhex(final_num)
65+
return bytes.fromhex(s)

src/hpack/huffman_constants.py

-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
# -*- coding: utf-8 -*-
21
"""
32
hpack/huffman_constants
43
~~~~~~~~~~~~~~~~~~~~~~~

0 commit comments

Comments
 (0)