Skip to content

Commit 2b32edc

Browse files
authored
refactor(mypy): add stricter rules to specific modules (#969)
1 parent 8ed3e4b commit 2b32edc

File tree

13 files changed

+97
-73
lines changed

13 files changed

+97
-73
lines changed

hathor/builder/builder.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,8 @@
4747
TransactionStorage,
4848
)
4949
from hathor.util import Random, get_environment_info, not_none
50-
from hathor.verification.verification_service import VerificationService, VertexVerifiers
50+
from hathor.verification.verification_service import VerificationService
51+
from hathor.verification.vertex_verifiers import VertexVerifiers
5152
from hathor.wallet import BaseWallet, Wallet
5253

5354
logger = get_logger()

hathor/cli/mining.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,8 @@ def execute(args: Namespace) -> None:
139139

140140
from hathor.conf.get_settings import get_global_settings
141141
from hathor.daa import DifficultyAdjustmentAlgorithm
142-
from hathor.verification.verification_service import VerificationService, VertexVerifiers
142+
from hathor.verification.verification_service import VerificationService
143+
from hathor.verification.vertex_verifiers import VertexVerifiers
143144
settings = get_global_settings()
144145
daa = DifficultyAdjustmentAlgorithm(settings=settings)
145146
verifiers = VertexVerifiers.create_defaults(settings=settings, daa=daa, feature_service=Mock())

hathor/consensus/block_consensus.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
# limitations under the License.
1414

1515
from itertools import chain
16-
from typing import TYPE_CHECKING, Iterable, Optional, cast
16+
from typing import TYPE_CHECKING, Any, Iterable, Optional, cast
1717

1818
from structlog import get_logger
1919

@@ -39,7 +39,7 @@ def __init__(self, context: 'ConsensusAlgorithmContext') -> None:
3939
self.context = context
4040

4141
@classproperty
42-
def log(cls):
42+
def log(cls) -> Any:
4343
""" This is a workaround because of a bug on structlog (or abc).
4444
4545
See: https://github.com/hynek/structlog/issues/229

hathor/consensus/transaction_consensus.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
# See the License for the specific language governing permissions and
1313
# limitations under the License.
1414

15-
from typing import TYPE_CHECKING, Iterable, cast
15+
from typing import TYPE_CHECKING, Any, Iterable, cast
1616

1717
from structlog import get_logger
1818

@@ -38,7 +38,7 @@ def __init__(self, context: 'ConsensusAlgorithmContext') -> None:
3838
self.context = context
3939

4040
@classproperty
41-
def log(cls):
41+
def log(cls) -> Any:
4242
""" This is a workaround because of a bug on structlog (or abc).
4343
4444
See: https://github.com/hynek/structlog/issues/229

hathor/event/model/base_event.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,11 @@
1212
# See the License for the specific language governing permissions and
1313
# limitations under the License.
1414

15-
from typing import Optional
15+
from typing import Any, Optional
1616

1717
from pydantic import NonNegativeInt, validator
1818

19-
from hathor.event.model.event_data import EventData
19+
from hathor.event.model.event_data import BaseEventData, EventData
2020
from hathor.event.model.event_type import EventType
2121
from hathor.pubsub import EventArguments
2222
from hathor.utils.pydantic import BaseModel
@@ -58,7 +58,7 @@ def from_event_arguments(
5858
)
5959

6060
@validator('data')
61-
def data_type_must_match_event_type(cls, v, values):
61+
def data_type_must_match_event_type(cls, v: BaseEventData, values: dict[str, Any]) -> BaseEventData:
6262
event_type = EventType(values['type'])
6363
expected_data_type = event_type.data_type()
6464

hathor/event/resources/event.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
from typing import Optional
1717

1818
from pydantic import Field, NonNegativeInt
19+
from twisted.web.http import Request
1920

2021
from hathor.api_util import Resource, set_cors
2122
from hathor.cli.openapi_files.register import register_resource
@@ -35,7 +36,7 @@ def __init__(self, event_manager: Optional[EventManager]):
3536
super().__init__()
3637
self.event_manager = event_manager
3738

38-
def render_GET(self, request):
39+
def render_GET(self, request: Request) -> bytes:
3940
request.setHeader(b'content-type', b'application/json; charset=utf-8')
4041
set_cors(request, 'GET')
4142

hathor/event/websocket/protocol.py

Lines changed: 8 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,14 @@
1212
# See the License for the specific language governing permissions and
1313
# limitations under the License.
1414

15-
from typing import TYPE_CHECKING, Callable, Optional
15+
from typing import TYPE_CHECKING, Optional
1616

1717
from autobahn.exception import Disconnected
1818
from autobahn.twisted.websocket import WebSocketServerProtocol
1919
from autobahn.websocket import ConnectionRequest
2020
from pydantic import ValidationError
2121
from structlog import get_logger
22+
from typing_extensions import assert_never
2223

2324
from hathor.event.websocket.request import AckRequest, Request, RequestWrapper, StartStreamRequest, StopStreamRequest
2425
from hathor.event.websocket.response import EventResponse, InvalidRequestResponse, InvalidRequestType, Response
@@ -50,7 +51,7 @@ class EventWebsocketProtocol(WebSocketServerProtocol):
5051
# Whether the stream is enabled or not.
5152
_stream_is_active: bool = False
5253

53-
def __init__(self):
54+
def __init__(self) -> None:
5455
super().__init__()
5556
self.log = logger.new()
5657

@@ -102,18 +103,11 @@ def onMessage(self, payload: bytes, isBinary: bool) -> None:
102103

103104
def _handle_request(self, request: Request) -> None:
104105
"""Handles a request message according to its type."""
105-
# This could be a pattern match in Python 3.10
106-
request_type = type(request)
107-
handlers: dict[type, Callable] = {
108-
StartStreamRequest: self._handle_start_stream_request,
109-
AckRequest: self._handle_ack_request,
110-
StopStreamRequest: lambda _: self._handle_stop_stream_request()
111-
}
112-
handle_fn = handlers.get(request_type)
113-
114-
assert handle_fn is not None, f'cannot handle request of unknown type "{request_type}"'
115-
116-
handle_fn(request)
106+
match request:
107+
case StartStreamRequest(): self._handle_start_stream_request(request)
108+
case AckRequest(): self._handle_ack_request(request)
109+
case StopStreamRequest(): self._handle_stop_stream_request()
110+
case _: assert_never(request)
117111

118112
def _handle_start_stream_request(self, request: StartStreamRequest) -> None:
119113
"""

hathor/event/websocket/request.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
# See the License for the specific language governing permissions and
1313
# limitations under the License.
1414

15-
from typing import Annotated, Literal, Optional, Union
15+
from typing import Annotated, Literal, Optional
1616

1717
from pydantic import Field, NonNegativeInt
1818

@@ -54,7 +54,7 @@ class StopStreamRequest(BaseModel):
5454
type: Literal['STOP_STREAM']
5555

5656

57-
Request = Annotated[Union[StartStreamRequest, AckRequest, StopStreamRequest], Field(discriminator='type')]
57+
Request = Annotated[StartStreamRequest | AckRequest | StopStreamRequest, Field(discriminator='type')]
5858

5959

6060
class RequestWrapper(BaseModel):

hathor/sysctl/sysctl.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
# See the License for the specific language governing permissions and
1313
# limitations under the License.
1414

15-
from typing import Any, Callable, Iterator, NamedTuple, Optional
15+
from typing import Any, Callable, Iterator, NamedTuple, Optional, ParamSpec, TypeVar
1616

1717
from pydantic import validate_arguments
1818
from structlog import get_logger
@@ -21,16 +21,18 @@
2121

2222
Getter = Callable[[], Any]
2323
Setter = Callable[..., None]
24+
P = ParamSpec('P')
25+
T = TypeVar('T')
2426

2527
logger = get_logger()
2628

2729

28-
def signal_handler_safe(f):
30+
def signal_handler_safe(f: Callable[P, T]) -> Callable[P, T]:
2931
"""Decorator to mark methods as signal handler safe.
3032
3133
It should only be used if that method can be executed during a signal handling.
3234
Notice that a signal handling can pause the code execution at any point and the execution will resume after."""
33-
f._signal_handler_safe = True
35+
f._signal_handler_safe = True # type: ignore[attr-defined]
3436
return f
3537

3638

hathor/types.py

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,15 @@
1212
# See the License for the specific language governing permissions and
1313
# limitations under the License.
1414

15+
from typing import TypeAlias
16+
1517
# XXX There is a lot of refactor to be done before we can use `NewType`.
1618
# So, let's skip using NewType until everything is refactored.
1719

18-
VertexId = bytes # NewType('TxId', bytes)
19-
Address = bytes # NewType('Address', bytes)
20-
AddressB58 = str
21-
TxOutputScript = bytes # NewType('TxOutputScript', bytes)
22-
Timestamp = int # NewType('Timestamp', int)
23-
TokenUid = VertexId # NewType('TokenUid', VertexId)
24-
Amount = int # NewType('Amount', int)
20+
VertexId: TypeAlias = bytes # NewType('TxId', bytes)
21+
Address: TypeAlias = bytes # NewType('Address', bytes)
22+
AddressB58: TypeAlias = str
23+
TxOutputScript: TypeAlias = bytes # NewType('TxOutputScript', bytes)
24+
Timestamp: TypeAlias = int # NewType('Timestamp', int)
25+
TokenUid: TypeAlias = VertexId # NewType('TokenUid', VertexId)
26+
Amount: TypeAlias = int # NewType('Amount', int)

poetry.lock

Lines changed: 34 additions & 34 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pyproject.toml

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,8 @@ hathor-cli = 'hathor.cli.main:main'
3838
[tool.poetry.dev-dependencies]
3939
flake8 = "~6.1.0"
4040
isort = {version = "~5.12.0", extras = ["colors"]}
41-
mypy = {version = "^1.5.1", markers = "implementation_name == 'cpython'"}
42-
mypy-zope = {version = "^1.0.1", markers = "implementation_name == 'cpython'"}
41+
mypy = {version = "^1.9.0", markers = "implementation_name == 'cpython'"}
42+
mypy-zope = {version = "^1.0.4", markers = "implementation_name == 'cpython'"}
4343
pytest = "~7.4.3"
4444
pytest-cov = "~4.1.0"
4545
flaky = "~3.7.0"
@@ -97,6 +97,8 @@ multi_line_output = 3
9797
pretty = true
9898
disallow_incomplete_defs = true
9999
no_implicit_optional = true
100+
extra_checks = true
101+
disallow_untyped_decorators = true
100102
warn_redundant_casts = true
101103
warn_unused_configs = true
102104
warn_unused_ignores = true
@@ -131,6 +133,27 @@ module = [
131133
]
132134
ignore_missing_imports = true
133135

136+
# This override enables stricter rules for some specific modules.
137+
# Currently, we have only two options from strict-mode that are disabled, but we have to opt-in instead of opt-out
138+
# because setting strict=true doesn't work for module-level settings.
139+
# Reference: https://mypy.readthedocs.io/en/stable/existing_code.html#introduce-stricter-options
140+
[[tool.mypy.overrides]]
141+
module = [
142+
"hathor.consensus.*",
143+
"hathor.feature_activation.*",
144+
"hathor.event.*",
145+
"hathor.verification.*"
146+
]
147+
strict_equality = true
148+
strict_concatenate = true
149+
check_untyped_defs = true
150+
disallow_any_generics = true
151+
disallow_untyped_defs = true
152+
no_implicit_reexport = true
153+
warn_return_any = true
154+
# disallow_subclassing_any = true
155+
# disallow_untyped_calls = true
156+
134157
[tool.pydantic-mypy]
135158
init_typed = true
136159
init_forbid_extra = true

tests/sysctl/test_sysctl.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313

1414
class SysctlTest(unittest.TestCase):
1515
# We need this patch because pydantic.validate_arguments fails when it gets a mock function.
16-
@patch('hathor.sysctl.sysctl.validate_arguments', new=lambda x: x)
16+
@patch('hathor.sysctl.sysctl.validate_arguments', new=lambda x: x) # type: ignore
1717
def setUp(self) -> None:
1818
super().setUp()
1919

0 commit comments

Comments
 (0)