Skip to content

Commit 1702e21

Browse files
committed
add typing information
1 parent cadf69e commit 1702e21

20 files changed

+93
-101
lines changed

Diff for: 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

Diff for: setup.py

+1-1
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',

Diff for: 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
~~~~~

Diff for: 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
~~~~~~~~~~~~~~~~~~~~~~~

Diff for: src/hpack/hpack.py

+47-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,7 @@ 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]]) -> Generator[Union[tuple[bytes, bytes], tuple[str, str]], None, None]:
132126
"""
133127
This converts a dictionary to an iterable of two-tuples. This is a
134128
HPACK-specific function because it pulls "special-headers" out first and
@@ -140,19 +134,19 @@ def _dict_to_iterable(header_dict):
140134
key=lambda k: not _to_bytes(k).startswith(b':')
141135
)
142136
for key in keys:
143-
yield key, header_dict[key]
137+
yield key, header_dict[key] # type: ignore
144138

145139

146-
def _to_bytes(value):
140+
def _to_bytes(value: Union[bytes, str, Any]) -> bytes:
147141
"""
148142
Convert anything to bytes through a UTF-8 encoded string
149143
"""
150144
t = type(value)
151145
if t is bytes:
152-
return value
146+
return value # type: ignore
153147
if t is not str:
154148
value = str(value)
155-
return value.encode("utf-8")
149+
return value.encode("utf-8") # type: ignore
156150

157151

158152
class Encoder:
@@ -161,27 +155,29 @@ class Encoder:
161155
HTTP/2 header blocks.
162156
"""
163157

164-
def __init__(self):
158+
def __init__(self) -> None:
165159
self.header_table = HeaderTable()
166160
self.huffman_coder = HuffmanEncoder(
167161
REQUEST_CODES, REQUEST_CODES_LENGTH
168162
)
169-
self.table_size_changes = []
163+
self.table_size_changes: list[int] = []
170164

171165
@property
172-
def header_table_size(self):
166+
def header_table_size(self) -> int:
173167
"""
174168
Controls the size of the HPACK header table.
175169
"""
176170
return self.header_table.maxsize
177171

178172
@header_table_size.setter
179-
def header_table_size(self, value):
173+
def header_table_size(self, value: int) -> None:
180174
self.header_table.maxsize = value
181175
if self.header_table.resized:
182176
self.table_size_changes.append(value)
183177

184-
def encode(self, headers, huffman=True):
178+
def encode(self,
179+
headers: Headers,
180+
huffman: bool = True) -> bytes:
185181
"""
186182
Takes a set of headers and encodes them into a HPACK-encoded header
187183
block.
@@ -256,13 +252,13 @@ def encode(self, headers, huffman=True):
256252
header = (_to_bytes(header[0]), _to_bytes(header[1]))
257253
header_block.append(self.add(header, sensitive, huffman))
258254

259-
header_block = b''.join(header_block)
255+
encoded = b''.join(header_block)
260256

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

263-
return header_block
259+
return encoded
264260

265-
def add(self, to_add, sensitive, huffman=False):
261+
def add(self, to_add: tuple[bytes, bytes], sensitive: bool, huffman: bool = False) -> bytes:
266262
"""
267263
This function takes a header key-value tuple and serializes it.
268264
"""
@@ -311,15 +307,15 @@ def add(self, to_add, sensitive, huffman=False):
311307

312308
return encoded
313309

314-
def _encode_indexed(self, index):
310+
def _encode_indexed(self, index: int) -> bytes:
315311
"""
316312
Encodes a header using the indexed representation.
317313
"""
318314
field = encode_integer(index, 7)
319315
field[0] |= 0x80 # we set the top bit
320316
return bytes(field)
321317

322-
def _encode_literal(self, name, value, indexbit, huffman=False):
318+
def _encode_literal(self, name: bytes, value: bytes, indexbit: bytes, huffman: bool = False) -> bytes:
323319
"""
324320
Encodes a header with a literal name and literal value. If ``indexing``
325321
is True, the header will be added to the header table: otherwise it
@@ -340,7 +336,7 @@ def _encode_literal(self, name, value, indexbit, huffman=False):
340336
[indexbit, bytes(name_len), name, bytes(value_len), value]
341337
)
342338

343-
def _encode_indexed_literal(self, index, value, indexbit, huffman=False):
339+
def _encode_indexed_literal(self, index: int, value: bytes, indexbit: bytes, huffman: bool = False) -> bytes:
344340
"""
345341
Encodes a header with an indexed name and a literal value and performs
346342
incremental indexing.
@@ -362,16 +358,16 @@ def _encode_indexed_literal(self, index, value, indexbit, huffman=False):
362358

363359
return b''.join([bytes(prefix), bytes(value_len), value])
364360

365-
def _encode_table_size_change(self):
361+
def _encode_table_size_change(self) -> bytes:
366362
"""
367363
Produces the encoded form of all header table size change context
368364
updates.
369365
"""
370366
block = b''
371367
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)
368+
b = encode_integer(size_bytes, 5)
369+
b[0] |= 0x20
370+
block += bytes(b)
375371
self.table_size_changes = []
376372
return block
377373

@@ -397,7 +393,7 @@ class Decoder:
397393
Defaults to 64kB.
398394
:type max_header_list_size: ``int``
399395
"""
400-
def __init__(self, max_header_list_size=DEFAULT_MAX_HEADER_LIST_SIZE):
396+
def __init__(self, max_header_list_size: int = DEFAULT_MAX_HEADER_LIST_SIZE) -> None:
401397
self.header_table = HeaderTable()
402398

403399
#: The maximum decompressed size we will allow for any single header
@@ -426,17 +422,17 @@ def __init__(self, max_header_list_size=DEFAULT_MAX_HEADER_LIST_SIZE):
426422
self.max_allowed_table_size = self.header_table.maxsize
427423

428424
@property
429-
def header_table_size(self):
425+
def header_table_size(self) -> int:
430426
"""
431427
Controls the size of the HPACK header table.
432428
"""
433429
return self.header_table.maxsize
434430

435431
@header_table_size.setter
436-
def header_table_size(self, value):
432+
def header_table_size(self, value: int) -> None:
437433
self.header_table.maxsize = value
438434

439-
def decode(self, data, raw=False):
435+
def decode(self, data: bytes, raw: bool = False) -> Headers:
440436
"""
441437
Takes an HPACK-encoded header block and decodes it into a header set.
442438
@@ -454,7 +450,7 @@ def decode(self, data, raw=False):
454450
log.debug("Decoding %s", data)
455451

456452
data_mem = memoryview(data)
457-
headers = []
453+
headers: list[HeaderTuple] = []
458454
data_len = len(data)
459455
inflated_size = 0
460456
current_index = 0
@@ -501,7 +497,7 @@ def decode(self, data, raw=False):
501497

502498
if header:
503499
headers.append(header)
504-
inflated_size += table_entry_size(*header)
500+
inflated_size += table_entry_size(header[0], header[1])
505501

506502
if inflated_size > self.max_header_list_size:
507503
raise OversizedHeaderListError(
@@ -521,7 +517,7 @@ def decode(self, data, raw=False):
521517
except UnicodeDecodeError:
522518
raise HPACKDecodingError("Unable to decode headers as UTF-8.")
523519

524-
def _assert_valid_table_size(self):
520+
def _assert_valid_table_size(self) -> None:
525521
"""
526522
Check that the table size set by the encoder is lower than the maximum
527523
we expect to have.
@@ -531,7 +527,7 @@ def _assert_valid_table_size(self):
531527
"Encoder did not shrink table size to within the max"
532528
)
533529

534-
def _update_encoding_context(self, data):
530+
def _update_encoding_context(self, data: bytes) -> int:
535531
"""
536532
Handles a byte that updates the encoding context.
537533
"""
@@ -544,7 +540,7 @@ def _update_encoding_context(self, data):
544540
self.header_table_size = new_size
545541
return consumed
546542

547-
def _decode_indexed(self, data):
543+
def _decode_indexed(self, data: bytes) -> tuple[HeaderTuple, int]:
548544
"""
549545
Decodes a header represented using the indexed representation.
550546
"""
@@ -553,13 +549,13 @@ def _decode_indexed(self, data):
553549
log.debug("Decoded %s, consumed %d", header, consumed)
554550
return header, consumed
555551

556-
def _decode_literal_no_index(self, data):
552+
def _decode_literal_no_index(self, data: bytes) -> tuple[HeaderTuple, int]:
557553
return self._decode_literal(data, False)
558554

559-
def _decode_literal_index(self, data):
555+
def _decode_literal_index(self, data: bytes) -> tuple[HeaderTuple, int]:
560556
return self._decode_literal(data, True)
561557

562-
def _decode_literal(self, data, should_index):
558+
def _decode_literal(self, data: bytes, should_index: bool) -> tuple[HeaderTuple, int]:
563559
"""
564560
Decodes a header represented with a literal.
565561
"""
@@ -577,7 +573,7 @@ def _decode_literal(self, data, should_index):
577573
high_byte = data[0]
578574
indexed_name = high_byte & 0x0F
579575
name_len = 4
580-
not_indexable = high_byte & 0x10
576+
not_indexable = bool(high_byte & 0x10)
581577

582578
if indexed_name:
583579
# Indexed header name.
@@ -616,6 +612,7 @@ def _decode_literal(self, data, should_index):
616612

617613
# If we have been told never to index the header field, encode that in
618614
# the tuple we use.
615+
header: HeaderTuple
619616
if not_indexable:
620617
header = NeverIndexedHeaderTuple(name, value)
621618
else:

Diff for: 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)

Diff for: 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)