Skip to content

Commit a9bda0e

Browse files
authored
Fix all typing issues (#134)
1 parent 28d1db1 commit a9bda0e

9 files changed

Lines changed: 141 additions & 64 deletions

File tree

.github/workflows/tests.yml

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,24 @@ on:
1010
- master
1111

1212
jobs:
13+
typecheck:
14+
runs-on: ubuntu-latest
15+
16+
steps:
17+
- uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1
18+
with:
19+
fetch-depth: 50
20+
submodules: true
21+
22+
- name: Set up Python ${{ matrix.python-version }}
23+
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
24+
with:
25+
python-version: "3.9"
26+
27+
- name: Type check
28+
run: make typecheck
29+
30+
1331
build:
1432
runs-on: ${{ matrix.os }}
1533
strategy:
@@ -37,7 +55,7 @@ jobs:
3755
__version__\s*=\s*(?:['"])([[:PEP440:]])(?:['"])
3856
3957
- name: Set up Python ${{ matrix.python-version }}
40-
uses: actions/setup-python@f677139bbe7f9c59b41e40162b753c062f5d49a3 # v5.2.0
58+
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
4159
if: steps.release.outputs.version == 0
4260
with:
4361
python-version: ${{ matrix.python-version }}

Makefile

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,14 @@ else
1212
endif
1313

1414
compile:
15-
$(PIP) install -e .
15+
$(PIP) install -e . --group dev
1616

1717
test: compile
1818
$(PYTHON) -m unittest -v
1919

20+
typecheck: compile
21+
$(PYTHON) -m pyright
22+
2023
clean:
2124
find $(ROOT)/httptools/parser -name '*.c' | xargs rm -f
2225
find $(ROOT)/httptools/parser -name '*.so' | xargs rm -f

httptools/__init__.py

Lines changed: 32 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,35 @@
11
from . import parser
2-
from .parser import * # NOQA
2+
from .parser import (
3+
HTTPProtocol,
4+
HttpRequestParser,
5+
HttpResponseParser,
6+
HttpParserError,
7+
HttpParserCallbackError,
8+
HttpParserInvalidStatusError,
9+
HttpParserInvalidMethodError,
10+
HttpParserInvalidURLError,
11+
HttpParserUpgrade,
12+
parse_url,
13+
)
314

4-
from ._version import __version__ # NOQA
15+
from ._version import __version__
516

6-
__all__ = parser.__all__ + ('__version__',) # NOQA
17+
__all__ = (
18+
"parser",
19+
# protocol
20+
"HTTPProtocol",
21+
# parser
22+
"HttpRequestParser",
23+
"HttpResponseParser",
24+
# errors
25+
"HttpParserError",
26+
"HttpParserCallbackError",
27+
"HttpParserInvalidStatusError",
28+
"HttpParserInvalidMethodError",
29+
"HttpParserInvalidURLError",
30+
"HttpParserUpgrade",
31+
# url parser
32+
"parse_url",
33+
# version
34+
"__version__",
35+
)

httptools/parser/__init__.py

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,28 @@
11
from .protocol import HTTPProtocol
2-
from .parser import * # NoQA
3-
from .errors import * # NoQA
4-
from .url_parser import * # NoQA
2+
from .parser import HttpRequestParser, HttpResponseParser # NoQA
3+
from .errors import (
4+
HttpParserError,
5+
HttpParserCallbackError,
6+
HttpParserInvalidStatusError,
7+
HttpParserInvalidMethodError,
8+
HttpParserInvalidURLError,
9+
HttpParserUpgrade,
10+
)
11+
from .url_parser import parse_url
512

6-
__all__ = parser.__all__ + errors.__all__ + url_parser.__all__ # NoQA
13+
__all__ = (
14+
# protocol
15+
"HTTPProtocol",
16+
# parser
17+
"HttpRequestParser",
18+
"HttpResponseParser",
19+
# errors
20+
"HttpParserError",
21+
"HttpParserCallbackError",
22+
"HttpParserInvalidStatusError",
23+
"HttpParserInvalidMethodError",
24+
"HttpParserInvalidURLError",
25+
"HttpParserUpgrade",
26+
# url_parser
27+
"parse_url",
28+
)

httptools/parser/parser.pyi

Lines changed: 29 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,57 +1,58 @@
1-
from typing import Union, Any
21
from array import array
32
from .protocol import HTTPProtocol
43

54
class HttpParser:
6-
def __init__(self, protocol: Union[HTTPProtocol, Any]) -> None:
7-
"""
8-
protocol -- a Python object with the following methods
9-
(all optional):
10-
11-
- on_message_begin()
12-
- on_url(url: bytes)
13-
- on_header(name: bytes, value: bytes)
14-
- on_headers_complete()
15-
- on_body(body: bytes)
16-
- on_message_complete()
17-
- on_chunk_header()
18-
- on_chunk_complete()
19-
- on_status(status: bytes)
5+
def __init__(self, protocol: HTTPProtocol | object) -> None:
6+
"""The HTTP parser.
7+
8+
Args:
9+
protocol (HTTPProtocol): Callback interface for the parser.
2010
"""
2111

12+
def set_dangerous_leniencies(
13+
self,
14+
lenient_headers: bool | None = None,
15+
lenient_chunked_length: bool | None = None,
16+
lenient_keep_alive: bool | None = None,
17+
lenient_transfer_encoding: bool | None = None,
18+
lenient_version: bool | None = None,
19+
lenient_data_after_close: bool | None = None,
20+
lenient_optional_lf_after_cr: bool | None = None,
21+
lenient_optional_cr_before_lf: bool | None = None,
22+
lenient_optional_crlf_after_chunk: bool | None = None,
23+
lenient_spaces_after_chunk_size: bool | None = None,
24+
) -> None:
25+
"""Set dangerous leniencies for the parser."""
26+
2227
def get_http_version(self) -> str:
23-
"""Return an HTTP protocol version."""
24-
...
28+
"""Retrieve the HTTP protocol version e.g. "1.1"."""
2529

2630
def should_keep_alive(self) -> bool:
27-
"""Return ``True`` if keep-alive mode is preferred."""
28-
...
31+
"""Return `True` if keep-alive mode is preferred."""
2932

3033
def should_upgrade(self) -> bool:
31-
"""Return ``True`` if the parsed request is a valid Upgrade request.
34+
"""Return `True` if the parsed request is a valid Upgrade request.
3235
The method exposes a flag set just before on_headers_complete.
3336
Calling this method earlier will only yield `False`."""
34-
...
3537

36-
def feed_data(self, data: Union[bytes, bytearray, memoryview, array]) -> None:
38+
def feed_data(self, data: bytes | bytearray | memoryview | array[int]) -> None:
3739
"""Feed data to the parser.
3840
39-
Will eventually trigger callbacks on the ``protocol``
40-
object.
41+
Will eventually trigger callbacks on the ``protocol`` object.
4142
4243
On HTTP upgrade, this method will raise an
4344
``HttpParserUpgrade`` exception, with its sole argument
4445
set to the offset of the non-HTTP data in ``data``.
4546
"""
4647

4748
class HttpRequestParser(HttpParser):
48-
"""Used for parsing http requests from the server's side"""
49+
"""Used for parsing http requests from the server side."""
4950

5051
def get_method(self) -> bytes:
51-
"""Return HTTP request method (GET, HEAD, etc)"""
52+
"""Retrieve the HTTP method of the request."""
5253

5354
class HttpResponseParser(HttpParser):
54-
"""Used for parsing http requests from the client's side"""
55+
"""Used for parsing http responses from the client side."""
5556

5657
def get_status_code(self) -> int:
57-
"""Return the status code of the HTTP response"""
58+
"""Retrieve the status code of the HTTP response."""

httptools/parser/protocol.py

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,12 @@
44
class HTTPProtocol(Protocol):
55
"""Used for providing static type-checking when parsing through the http protocol"""
66

7-
def on_message_begin() -> None: ...
8-
def on_url(url: bytes) -> None: ...
9-
def on_header(name: bytes, value: bytes) -> None: ...
10-
def on_headers_complete() -> None: ...
11-
def on_body(body: bytes) -> None: ...
12-
def on_message_complete() -> None: ...
13-
def on_chunk_header() -> None: ...
14-
def on_chunk_complete() -> None: ...
15-
def on_status(status: bytes) -> None: ...
7+
def on_message_begin(self) -> None: ...
8+
def on_url(self, url: bytes) -> None: ...
9+
def on_header(self, name: bytes, value: bytes) -> None: ...
10+
def on_headers_complete(self) -> None: ...
11+
def on_body(self, body: bytes) -> None: ...
12+
def on_message_complete(self) -> None: ...
13+
def on_chunk_header(self) -> None: ...
14+
def on_chunk_complete(self) -> None: ...
15+
def on_status(self, status: bytes) -> None: ...

httptools/parser/url_parser.pyi

Lines changed: 2 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
from typing import Union
21
from array import array
32

43
class URL:
@@ -10,18 +9,5 @@ class URL:
109
fragment: bytes
1110
userinfo: bytes
1211

13-
def parse_url(url: Union[bytes, bytearray, memoryview, array]) -> URL:
14-
"""Parse URL strings into a structured Python object.
15-
16-
Returns an instance of ``httptools.URL`` class with the
17-
following attributes:
18-
19-
- schema: bytes
20-
- host: bytes
21-
- port: int
22-
- path: bytes
23-
- query: bytes
24-
- fragment: bytes
25-
- userinfo: bytes
26-
"""
27-
...
12+
def parse_url(url: bytes | bytearray | memoryview | array[int]) -> URL:
13+
"""Parse a URL string into a structured Python object."""

httptools/py.typed

Whitespace-only changes.

pyproject.toml

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,5 +29,23 @@ readme = "README.md"
2929
[project.urls]
3030
Homepage = "https://github.com/MagicStack/httptools"
3131

32-
[project.optional-dependencies]
33-
test = [] # for backward compatibility
32+
[dependency-groups]
33+
dev = [
34+
# type checker
35+
"pyright >= 1.1.406",
36+
# tests
37+
"pytest",
38+
# build
39+
"setuptools",
40+
"wheel"
41+
]
42+
43+
[tool.pyright]
44+
pythonVersion = "3.8"
45+
typeCheckingMode = "strict"
46+
reportMissingTypeStubs = false
47+
reportUnnecessaryIsInstance = false
48+
reportUnnecessaryTypeIgnoreComment = true
49+
reportMissingModuleSource = false
50+
include = ["httptools"]
51+
exclude = ["tests"]

0 commit comments

Comments
 (0)