Skip to content
This repository was archived by the owner on Apr 26, 2024. It is now read-only.

Commit ffc2ee5

Browse files
author
David Robertson
authored
Use mypy 1.0 (#15052)
* Update mypy and mypy-zope * Remove unused ignores These used to suppress ``` synapse/storage/engines/__init__.py:28: error: "__new__" must return a class instance (got "NoReturn") [misc] ``` and ``` synapse/http/matrixfederationclient.py:1270: error: "BaseException" has no attribute "reasons" [attr-defined] ``` (note that we check `hasattr(e, "reasons")` above) * Avoid empty body warnings, sometimes by marking methods as abstract E.g. ``` tests/handlers/test_register.py:58: error: Missing return statement [empty-body] tests/handlers/test_register.py:108: error: Missing return statement [empty-body] ``` * Suppress false positive about `JaegerConfig` Complaint was ``` synapse/logging/opentracing.py:450: error: Function "Type[Config]" could always be true in boolean context [truthy-function] ``` * Fix not calling `is_state()` Oops! ``` tests/rest/client/test_third_party_rules.py:428: error: Function "Callable[[], bool]" could always be true in boolean context [truthy-function] ``` * Suppress false positives from ParamSpecs ```` synapse/logging/opentracing.py:971: error: Argument 2 to "_custom_sync_async_decorator" has incompatible type "Callable[[Arg(Callable[P, R], 'func'), **P], _GeneratorContextManager[None]]"; expected "Callable[[Callable[P, R], **P], _GeneratorContextManager[None]]" [arg-type] synapse/logging/opentracing.py:1017: error: Argument 2 to "_custom_sync_async_decorator" has incompatible type "Callable[[Arg(Callable[P, R], 'func'), **P], _GeneratorContextManager[None]]"; expected "Callable[[Callable[P, R], **P], _GeneratorContextManager[None]]" [arg-type] ```` * Drive-by improvement to `wrapping_logic` annotation * Workaround false "unreachable" positives See Shoobx/mypy-zope#91 ``` tests/http/test_proxyagent.py:626: error: Statement is unreachable [unreachable] tests/http/test_proxyagent.py:762: error: Statement is unreachable [unreachable] tests/http/test_proxyagent.py:826: error: Statement is unreachable [unreachable] tests/http/test_proxyagent.py:838: error: Statement is unreachable [unreachable] tests/http/test_proxyagent.py:845: error: Statement is unreachable [unreachable] tests/http/federation/test_matrix_federation_agent.py:151: error: Statement is unreachable [unreachable] tests/http/federation/test_matrix_federation_agent.py:452: error: Statement is unreachable [unreachable] tests/logging/test_remote_handler.py:60: error: Statement is unreachable [unreachable] tests/logging/test_remote_handler.py:93: error: Statement is unreachable [unreachable] tests/logging/test_remote_handler.py:127: error: Statement is unreachable [unreachable] tests/logging/test_remote_handler.py:152: error: Statement is unreachable [unreachable] ``` * Changelog * Tweak DBAPI2 Protocol to be accepted by mypy 1.0 Some extra context in: - matrix-org/python-canonicaljson#57 - python/mypy#6002 - https://mypy.readthedocs.io/en/latest/common_issues.html#covariant-subtyping-of-mutable-protocol-members-is-rejected * Pull in updated canonicaljson lib so the protocol check just works * Improve comments in opentracing I tried to workaround the ignores but found it too much trouble. I think the corresponding issue is python/mypy#12909. The mypy repo has a PR claiming to fix this (python/mypy#14677) which might mean this gets resolved soon? * Better annotation for INTERACTIVE_AUTH_CHECKERS * Drive-by AUTH_TYPE annotation, to remove an ignore
1 parent 979f237 commit ffc2ee5

File tree

17 files changed

+209
-104
lines changed

17 files changed

+209
-104
lines changed

changelog.d/15052.misc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Improve type hints.

poetry.lock

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

synapse/handlers/auth.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -201,7 +201,7 @@ def __init__(self, hs: "HomeServer"):
201201
for auth_checker_class in INTERACTIVE_AUTH_CHECKERS:
202202
inst = auth_checker_class(hs)
203203
if inst.is_enabled():
204-
self.checkers[inst.AUTH_TYPE] = inst # type: ignore
204+
self.checkers[inst.AUTH_TYPE] = inst
205205

206206
self.bcrypt_rounds = hs.config.registration.bcrypt_rounds
207207

synapse/handlers/ui_auth/checkers.py

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

1515
import logging
16-
from typing import TYPE_CHECKING, Any
16+
from abc import ABC, abstractmethod
17+
from typing import TYPE_CHECKING, Any, ClassVar, Sequence, Type
1718

1819
from twisted.web.client import PartialDownloadError
1920

@@ -27,19 +28,28 @@
2728
logger = logging.getLogger(__name__)
2829

2930

30-
class UserInteractiveAuthChecker:
31+
class UserInteractiveAuthChecker(ABC):
3132
"""Abstract base class for an interactive auth checker"""
3233

33-
def __init__(self, hs: "HomeServer"):
34+
# This should really be an "abstract class property", i.e. it should
35+
# be an error to instantiate a subclass that doesn't specify an AUTH_TYPE.
36+
# But calling this a `ClassVar` is simpler than a decorator stack of
37+
# @property @abstractmethod and @classmethod (if that's even the right order).
38+
AUTH_TYPE: ClassVar[str]
39+
40+
def __init__(self, hs: "HomeServer"): # noqa: B027
3441
pass
3542

43+
@abstractmethod
3644
def is_enabled(self) -> bool:
3745
"""Check if the configuration of the homeserver allows this checker to work
3846
3947
Returns:
4048
True if this login type is enabled.
4149
"""
50+
raise NotImplementedError()
4251

52+
@abstractmethod
4353
async def check_auth(self, authdict: dict, clientip: str) -> Any:
4454
"""Given the authentication dict from the client, attempt to check this step
4555
@@ -304,7 +314,7 @@ async def check_auth(self, authdict: dict, clientip: str) -> Any:
304314
)
305315

306316

307-
INTERACTIVE_AUTH_CHECKERS = [
317+
INTERACTIVE_AUTH_CHECKERS: Sequence[Type[UserInteractiveAuthChecker]] = [
308318
DummyAuthChecker,
309319
TermsAuthChecker,
310320
RecaptchaAuthChecker,

synapse/http/matrixfederationclient.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1267,7 +1267,7 @@ async def get_file(
12671267
def _flatten_response_never_received(e: BaseException) -> str:
12681268
if hasattr(e, "reasons"):
12691269
reasons = ", ".join(
1270-
_flatten_response_never_received(f.value) for f in e.reasons # type: ignore[attr-defined]
1270+
_flatten_response_never_received(f.value) for f in e.reasons
12711271
)
12721272

12731273
return "%s:[%s]" % (type(e).__name__, reasons)

synapse/logging/opentracing.py

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -188,7 +188,7 @@ def set_fates(clotho, lachesis, atropos, father="Zues", mother="Themis"):
188188
)
189189

190190
import attr
191-
from typing_extensions import ParamSpec
191+
from typing_extensions import Concatenate, ParamSpec
192192

193193
from twisted.internet import defer
194194
from twisted.web.http import Request
@@ -445,7 +445,7 @@ def init_tracer(hs: "HomeServer") -> None:
445445
opentracing = None # type: ignore[assignment]
446446
return
447447

448-
if not opentracing or not JaegerConfig:
448+
if opentracing is None or JaegerConfig is None:
449449
raise ConfigError(
450450
"The server has been configured to use opentracing but opentracing is not "
451451
"installed."
@@ -872,7 +872,7 @@ def extract_text_map(carrier: Dict[str, str]) -> Optional["opentracing.SpanConte
872872

873873
def _custom_sync_async_decorator(
874874
func: Callable[P, R],
875-
wrapping_logic: Callable[[Callable[P, R], Any, Any], ContextManager[None]],
875+
wrapping_logic: Callable[Concatenate[Callable[P, R], P], ContextManager[None]],
876876
) -> Callable[P, R]:
877877
"""
878878
Decorates a function that is sync or async (coroutines), or that returns a Twisted
@@ -902,10 +902,14 @@ def _wrapping_logic(func: Callable[P, R], *args: P.args, **kwargs: P.kwargs) ->
902902
"""
903903

904904
if inspect.iscoroutinefunction(func):
905-
905+
# In this branch, R = Awaitable[RInner], for some other type RInner
906906
@wraps(func)
907-
async def _wrapper(*args: P.args, **kwargs: P.kwargs) -> R:
907+
async def _wrapper(
908+
*args: P.args, **kwargs: P.kwargs
909+
) -> Any: # Return type is RInner
908910
with wrapping_logic(func, *args, **kwargs):
911+
# type-ignore: func() returns R, but mypy doesn't know that R is
912+
# Awaitable here.
909913
return await func(*args, **kwargs) # type: ignore[misc]
910914

911915
else:
@@ -972,7 +976,11 @@ def _decorator(func: Callable[P, R]) -> Callable[P, R]:
972976
if not opentracing:
973977
return func
974978

975-
return _custom_sync_async_decorator(func, _wrapping_logic)
979+
# type-ignore: mypy seems to be confused by the ParamSpecs here.
980+
# I think the problem is https://github.com/python/mypy/issues/12909
981+
return _custom_sync_async_decorator(
982+
func, _wrapping_logic # type: ignore[arg-type]
983+
)
976984

977985
return _decorator
978986

@@ -1018,7 +1026,9 @@ def _wrapping_logic(
10181026
set_tag(SynapseTags.FUNC_KWARGS, str(kwargs))
10191027
yield
10201028

1021-
return _custom_sync_async_decorator(func, _wrapping_logic)
1029+
# type-ignore: mypy seems to be confused by the ParamSpecs here.
1030+
# I think the problem is https://github.com/python/mypy/issues/12909
1031+
return _custom_sync_async_decorator(func, _wrapping_logic) # type: ignore[arg-type]
10221032

10231033

10241034
@contextlib.contextmanager

synapse/rest/media/v1/_base.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
import logging
1717
import os
1818
import urllib
19+
from abc import ABC, abstractmethod
1920
from types import TracebackType
2021
from typing import Awaitable, Dict, Generator, List, Optional, Tuple, Type
2122

@@ -284,13 +285,14 @@ async def respond_with_responder(
284285
finish_request(request)
285286

286287

287-
class Responder:
288+
class Responder(ABC):
288289
"""Represents a response that can be streamed to the requester.
289290
290291
Responder is a context manager which *must* be used, so that any resources
291292
held can be cleaned up.
292293
"""
293294

295+
@abstractmethod
294296
def write_to_consumer(self, consumer: IConsumer) -> Awaitable:
295297
"""Stream response into consumer
296298
@@ -300,11 +302,12 @@ def write_to_consumer(self, consumer: IConsumer) -> Awaitable:
300302
Returns:
301303
Resolves once the response has finished being written
302304
"""
305+
raise NotImplementedError()
303306

304-
def __enter__(self) -> None:
307+
def __enter__(self) -> None: # noqa: B027
305308
pass
306309

307-
def __exit__(
310+
def __exit__( # noqa: B027
308311
self,
309312
exc_type: Optional[Type[BaseException]],
310313
exc_val: Optional[BaseException],

synapse/storage/engines/__init__.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
except ImportError:
2626

2727
class PostgresEngine(BaseDatabaseEngine): # type: ignore[no-redef]
28-
def __new__(cls, *args: object, **kwargs: object) -> NoReturn: # type: ignore[misc]
28+
def __new__(cls, *args: object, **kwargs: object) -> NoReturn:
2929
raise RuntimeError(
3030
f"Cannot create {cls.__name__} -- psycopg2 module is not installed"
3131
)
@@ -36,7 +36,7 @@ def __new__(cls, *args: object, **kwargs: object) -> NoReturn: # type: ignore[m
3636
except ImportError:
3737

3838
class Sqlite3Engine(BaseDatabaseEngine): # type: ignore[no-redef]
39-
def __new__(cls, *args: object, **kwargs: object) -> NoReturn: # type: ignore[misc]
39+
def __new__(cls, *args: object, **kwargs: object) -> NoReturn:
4040
raise RuntimeError(
4141
f"Cannot create {cls.__name__} -- sqlite3 module is not installed"
4242
)

0 commit comments

Comments
 (0)