Skip to content

Commit dbda322

Browse files
committed
add typing information
1 parent f5cf6b9 commit dbda322

20 files changed

+77
-95
lines changed

setup.cfg

Lines changed: 1 addition & 0 deletions
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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@
2929
author_email='cory@lukasa.co.uk',
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

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

src/hpack/exceptions.py

Lines changed: 0 additions & 1 deletion
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

Lines changed: 43 additions & 45 deletions
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 (
@@ -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
"""
4842
name = bytes(header[0])
4943
value = bytes(header[1])
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: dict[Union[bytes, str], Union[bytes, str]]) \
126+
-> Generator[tuple[Union[bytes, str], Union[bytes, 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
@@ -143,11 +138,11 @@ def _dict_to_iterable(header_dict):
143138
yield key, header_dict[key]
144139

145140

146-
def _to_bytes(string):
141+
def _to_bytes(string: Union[bytes, str, Any]) -> bytes:
147142
"""
148143
Convert string to bytes.
149144
"""
150-
if not isinstance(string, basestring): # pragma: no cover
145+
if not isinstance(string, (str, bytes)): # pragma: no cover
151146
string = str(string)
152147

153148
return string if isinstance(string, bytes) else string.encode('utf-8')
@@ -159,27 +154,29 @@ class Encoder:
159154
HTTP/2 header blocks.
160155
"""
161156

162-
def __init__(self):
157+
def __init__(self) -> None:
163158
self.header_table = HeaderTable()
164159
self.huffman_coder = HuffmanEncoder(
165160
REQUEST_CODES, REQUEST_CODES_LENGTH
166161
)
167-
self.table_size_changes = []
162+
self.table_size_changes: list[int] = []
168163

169164
@property
170-
def header_table_size(self):
165+
def header_table_size(self) -> int:
171166
"""
172167
Controls the size of the HPACK header table.
173168
"""
174169
return self.header_table.maxsize
175170

176171
@header_table_size.setter
177-
def header_table_size(self, value):
172+
def header_table_size(self, value: int) -> None:
178173
self.header_table.maxsize = value
179174
if self.header_table.resized:
180175
self.table_size_changes.append(value)
181176

182-
def encode(self, headers, huffman=True):
177+
def encode(self,
178+
headers: list[Union[HeaderTuple, tuple[bytes, bytes], dict[Any, Any]]],
179+
huffman: bool = True) -> bytes:
183180
"""
184181
Takes a set of headers and encodes them into a HPACK-encoded header
185182
block.
@@ -254,13 +251,13 @@ def encode(self, headers, huffman=True):
254251
header = (_to_bytes(header[0]), _to_bytes(header[1]))
255252
header_block.append(self.add(header, sensitive, huffman))
256253

257-
header_block = b''.join(header_block)
254+
encoded = b''.join(header_block)
258255

259-
log.debug("Encoded header block to %s", header_block)
256+
log.debug("Encoded header block to %s", encoded)
260257

261-
return header_block
258+
return encoded
262259

263-
def add(self, to_add, sensitive, huffman=False):
260+
def add(self, to_add: tuple[bytes, bytes], sensitive: bool, huffman: bool = False) -> bytes:
264261
"""
265262
This function takes a header key-value tuple and serializes it.
266263
"""
@@ -309,15 +306,15 @@ def add(self, to_add, sensitive, huffman=False):
309306

310307
return encoded
311308

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

320-
def _encode_literal(self, name, value, indexbit, huffman=False):
317+
def _encode_literal(self, name: bytes, value: bytes, indexbit: bytes, huffman: bool = False) -> bytes:
321318
"""
322319
Encodes a header with a literal name and literal value. If ``indexing``
323320
is True, the header will be added to the header table: otherwise it
@@ -338,7 +335,7 @@ def _encode_literal(self, name, value, indexbit, huffman=False):
338335
[indexbit, bytes(name_len), name, bytes(value_len), value]
339336
)
340337

341-
def _encode_indexed_literal(self, index, value, indexbit, huffman=False):
338+
def _encode_indexed_literal(self, index: int, value: bytes, indexbit: bytes, huffman: bool = False) -> bytes:
342339
"""
343340
Encodes a header with an indexed name and a literal value and performs
344341
incremental indexing.
@@ -360,16 +357,16 @@ def _encode_indexed_literal(self, index, value, indexbit, huffman=False):
360357

361358
return b''.join([bytes(prefix), bytes(value_len), value])
362359

363-
def _encode_table_size_change(self):
360+
def _encode_table_size_change(self) -> bytes:
364361
"""
365362
Produces the encoded form of all header table size change context
366363
updates.
367364
"""
368365
block = b''
369366
for size_bytes in self.table_size_changes:
370-
size_bytes = encode_integer(size_bytes, 5)
371-
size_bytes[0] |= 0x20
372-
block += bytes(size_bytes)
367+
b = encode_integer(size_bytes, 5)
368+
b[0] |= 0x20
369+
block += bytes(b)
373370
self.table_size_changes = []
374371
return block
375372

@@ -395,7 +392,7 @@ class Decoder:
395392
Defaults to 64kB.
396393
:type max_header_list_size: ``int``
397394
"""
398-
def __init__(self, max_header_list_size=DEFAULT_MAX_HEADER_LIST_SIZE):
395+
def __init__(self, max_header_list_size: int = DEFAULT_MAX_HEADER_LIST_SIZE) -> None:
399396
self.header_table = HeaderTable()
400397

401398
#: The maximum decompressed size we will allow for any single header
@@ -424,17 +421,17 @@ def __init__(self, max_header_list_size=DEFAULT_MAX_HEADER_LIST_SIZE):
424421
self.max_allowed_table_size = self.header_table.maxsize
425422

426423
@property
427-
def header_table_size(self):
424+
def header_table_size(self) -> int:
428425
"""
429426
Controls the size of the HPACK header table.
430427
"""
431428
return self.header_table.maxsize
432429

433430
@header_table_size.setter
434-
def header_table_size(self, value):
431+
def header_table_size(self, value: int) -> None:
435432
self.header_table.maxsize = value
436433

437-
def decode(self, data, raw=False):
434+
def decode(self, data: bytes, raw: bool = False) -> list[Union[HeaderTuple, NeverIndexedHeaderTuple]]:
438435
"""
439436
Takes an HPACK-encoded header block and decodes it into a header set.
440437
@@ -452,7 +449,7 @@ def decode(self, data, raw=False):
452449
log.debug("Decoding %s", data)
453450

454451
data_mem = memoryview(data)
455-
headers = []
452+
headers: list[Union[HeaderTuple, NeverIndexedHeaderTuple]] = []
456453
data_len = len(data)
457454
inflated_size = 0
458455
current_index = 0
@@ -499,7 +496,7 @@ def decode(self, data, raw=False):
499496

500497
if header:
501498
headers.append(header)
502-
inflated_size += table_entry_size(*header)
499+
inflated_size += table_entry_size(header[0], header[1])
503500

504501
if inflated_size > self.max_header_list_size:
505502
raise OversizedHeaderListError(
@@ -519,7 +516,7 @@ def decode(self, data, raw=False):
519516
except UnicodeDecodeError:
520517
raise HPACKDecodingError("Unable to decode headers as UTF-8.")
521518

522-
def _assert_valid_table_size(self):
519+
def _assert_valid_table_size(self) -> None:
523520
"""
524521
Check that the table size set by the encoder is lower than the maximum
525522
we expect to have.
@@ -529,7 +526,7 @@ def _assert_valid_table_size(self):
529526
"Encoder did not shrink table size to within the max"
530527
)
531528

532-
def _update_encoding_context(self, data):
529+
def _update_encoding_context(self, data: bytes) -> int:
533530
"""
534531
Handles a byte that updates the encoding context.
535532
"""
@@ -542,7 +539,7 @@ def _update_encoding_context(self, data):
542539
self.header_table_size = new_size
543540
return consumed
544541

545-
def _decode_indexed(self, data):
542+
def _decode_indexed(self, data: bytes) -> tuple[HeaderTuple, int]:
546543
"""
547544
Decodes a header represented using the indexed representation.
548545
"""
@@ -551,13 +548,13 @@ def _decode_indexed(self, data):
551548
log.debug("Decoded %s, consumed %d", header, consumed)
552549
return header, consumed
553550

554-
def _decode_literal_no_index(self, data):
551+
def _decode_literal_no_index(self, data: bytes) -> tuple[HeaderTuple, int]:
555552
return self._decode_literal(data, False)
556553

557-
def _decode_literal_index(self, data):
554+
def _decode_literal_index(self, data: bytes) -> tuple[HeaderTuple, int]:
558555
return self._decode_literal(data, True)
559556

560-
def _decode_literal(self, data, should_index):
557+
def _decode_literal(self, data: bytes, should_index: bool) -> tuple[HeaderTuple, int]:
561558
"""
562559
Decodes a header represented with a literal.
563560
"""
@@ -575,7 +572,7 @@ def _decode_literal(self, data, should_index):
575572
high_byte = data[0]
576573
indexed_name = high_byte & 0x0F
577574
name_len = 4
578-
not_indexable = high_byte & 0x10
575+
not_indexable = bool(high_byte & 0x10)
579576

580577
if indexed_name:
581578
# Indexed header name.
@@ -614,6 +611,7 @@ def _decode_literal(self, data, should_index):
614611

615612
# If we have been told never to index the header field, encode that in
616613
# the tuple we use.
614+
header: HeaderTuple
617615
if not_indexable:
618616
header = NeverIndexedHeaderTuple(name, value)
619617
else:

src/hpack/huffman.py

Lines changed: 8 additions & 9 deletions
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

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

src/hpack/huffman_table.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
# -*- coding: utf-8 -*-
21
"""
32
hpack/huffman_table
43
~~~~~~~~~~~~~~~~~~~
@@ -70,13 +69,15 @@
7069
loops at the Python-level is not too expensive. The total number of loop
7170
iterations is 4x the number of bytes passed to the decoder.
7271
"""
72+
from typing import Optional, Union
73+
7374
from .exceptions import HPACKDecodingError
7475

7576

7677
# This defines the state machine "class" at the top of the file. The reason we
7778
# do this is to keep the terrifing monster state table at the *bottom* of the
7879
# file so you don't have to actually *look* at the damn thing.
79-
def decode_huffman(huffman_string):
80+
def decode_huffman(huffman_string: Optional[Union[bytes, bytearray]]) -> bytes:
8081
"""
8182
Given a bytestring of Huffman-encoded data for HPACK, returns a bytestring
8283
of the decompressed data.

src/hpack/py.typed

Whitespace-only changes.

0 commit comments

Comments
 (0)