diff --git a/.github/workflows/ci-build.yml b/.github/workflows/ci-build.yml index 6555a6531..6c4cd5a6a 100644 --- a/.github/workflows/ci-build.yml +++ b/.github/workflows/ci-build.yml @@ -44,8 +44,19 @@ jobs: uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 with: python-version: ${{ env.LATEST_SUPPORTED_PY }} - - name: Run mypy verification - run: ./scripts/run_mypy.sh + - name: Install synchronous dependencies + run: | + pip install -U pip + pip install -U . + pip install -r requirements/tools.txt + - name: Type check synchronous modules + run: mypy --config-file pyproject.toml --exclude "async_|/adapter/" + - name: Install async and adapter dependencies + run: | + pip install -r requirements/async.txt + pip install -r requirements/adapter.txt + - name: Type check all modules + run: mypy --config-file pyproject.toml unittest: name: Unit tests diff --git a/slack_bolt/app/async_server.py b/slack_bolt/app/async_server.py index 998cd5a4b..f21d35932 100644 --- a/slack_bolt/app/async_server.py +++ b/slack_bolt/app/async_server.py @@ -1,5 +1,5 @@ import logging -from typing import Optional +from typing import Optional, TYPE_CHECKING from aiohttp import web @@ -7,19 +7,22 @@ from slack_bolt.response import BoltResponse from slack_bolt.util.utils import get_boot_message +if TYPE_CHECKING: + from slack_bolt.app.async_app import AsyncApp + class AsyncSlackAppServer: port: int path: str host: str - bolt_app: "AsyncApp" # type: ignore[name-defined] + bolt_app: "AsyncApp" web_app: web.Application def __init__( self, port: int, path: str, - app: "AsyncApp", # type: ignore[name-defined] + app: "AsyncApp", host: Optional[str] = None, ): """Standalone AIOHTTP Web Server. @@ -34,7 +37,7 @@ def __init__( self.port = port self.path = path self.host = host if host is not None else "0.0.0.0" - self.bolt_app: "AsyncApp" = app # type: ignore[name-defined] + self.bolt_app: "AsyncApp" = app self.web_app = web.Application() self._bolt_oauth_flow = self.bolt_app.oauth_flow if self._bolt_oauth_flow: diff --git a/slack_bolt/context/async_context.py b/slack_bolt/context/async_context.py index 33f260d38..94b2b5cbe 100644 --- a/slack_bolt/context/async_context.py +++ b/slack_bolt/context/async_context.py @@ -1,4 +1,4 @@ -from typing import Optional +from typing import Optional, TYPE_CHECKING from slack_sdk.web.async_client import AsyncWebClient @@ -16,6 +16,9 @@ from slack_bolt.context.set_title.async_set_title import AsyncSetTitle from slack_bolt.util.utils import create_copy +if TYPE_CHECKING: + from slack_bolt.listener.asyncio_runner import AsyncioListenerRunner + class AsyncBoltContext(BaseContext): """Context object associated with a request from Slack.""" @@ -42,7 +45,7 @@ def to_copyable(self) -> "AsyncBoltContext": # The return type is intentionally string to avoid circular imports @property - def listener_runner(self) -> "AsyncioListenerRunner": # type: ignore[name-defined] + def listener_runner(self) -> "AsyncioListenerRunner": """The properly configured listener_runner that is available for middleware/listeners.""" return self["listener_runner"] diff --git a/slack_bolt/context/context.py b/slack_bolt/context/context.py index 6184d5083..b101460a5 100644 --- a/slack_bolt/context/context.py +++ b/slack_bolt/context/context.py @@ -1,4 +1,4 @@ -from typing import Optional +from typing import Optional, TYPE_CHECKING from slack_sdk import WebClient @@ -16,6 +16,9 @@ from slack_bolt.context.set_title import SetTitle from slack_bolt.util.utils import create_copy +if TYPE_CHECKING: + from slack_bolt.listener.thread_runner import ThreadListenerRunner + class BoltContext(BaseContext): """Context object associated with a request from Slack.""" @@ -43,7 +46,7 @@ def to_copyable(self) -> "BoltContext": # The return type is intentionally string to avoid circular imports @property - def listener_runner(self) -> "ThreadListenerRunner": # type: ignore[name-defined] + def listener_runner(self) -> "ThreadListenerRunner": """The properly configured listener_runner that is available for middleware/listeners.""" return self["listener_runner"] diff --git a/slack_bolt/listener/async_listener_error_handler.py b/slack_bolt/listener/async_listener_error_handler.py index 88f4b3510..b1a73458e 100644 --- a/slack_bolt/listener/async_listener_error_handler.py +++ b/slack_bolt/listener/async_listener_error_handler.py @@ -48,9 +48,10 @@ async def handle( ) returned_response = await self.func(**kwargs) if returned_response is not None and isinstance(returned_response, BoltResponse): - response.status = returned_response.status # type: ignore[union-attr] - response.headers = returned_response.headers # type: ignore[union-attr] - response.body = returned_response.body # type: ignore[union-attr] + assert response is not None, "response must be provided when returning a BoltResponse from an error handler" + response.status = returned_response.status + response.headers = returned_response.headers + response.body = returned_response.body class AsyncDefaultListenerErrorHandler(AsyncListenerErrorHandler): diff --git a/slack_bolt/listener/listener_error_handler.py b/slack_bolt/listener/listener_error_handler.py index 0ad98f738..7dd6d066b 100644 --- a/slack_bolt/listener/listener_error_handler.py +++ b/slack_bolt/listener/listener_error_handler.py @@ -48,9 +48,10 @@ def handle( ) returned_response = self.func(**kwargs) if returned_response is not None and isinstance(returned_response, BoltResponse): - response.status = returned_response.status # type: ignore[union-attr] - response.headers = returned_response.headers # type: ignore[union-attr] - response.body = returned_response.body # type: ignore[union-attr] + assert response is not None, "response must be provided when returning a BoltResponse from an error handler" + response.status = returned_response.status + response.headers = returned_response.headers + response.body = returned_response.body class DefaultListenerErrorHandler(ListenerErrorHandler): diff --git a/slack_bolt/middleware/async_middleware_error_handler.py b/slack_bolt/middleware/async_middleware_error_handler.py index 1957d3ab6..932b0770b 100644 --- a/slack_bolt/middleware/async_middleware_error_handler.py +++ b/slack_bolt/middleware/async_middleware_error_handler.py @@ -48,9 +48,10 @@ async def handle( ) returned_response = await self.func(**kwargs) if returned_response is not None and isinstance(returned_response, BoltResponse): - response.status = returned_response.status # type: ignore[union-attr] - response.headers = returned_response.headers # type: ignore[union-attr] - response.body = returned_response.body # type: ignore[union-attr] + assert response is not None, "response must be provided when returning a BoltResponse from an error handler" + response.status = returned_response.status + response.headers = returned_response.headers + response.body = returned_response.body class AsyncDefaultMiddlewareErrorHandler(AsyncMiddlewareErrorHandler): diff --git a/slack_bolt/middleware/middleware_error_handler.py b/slack_bolt/middleware/middleware_error_handler.py index fe57e400c..5919414bb 100644 --- a/slack_bolt/middleware/middleware_error_handler.py +++ b/slack_bolt/middleware/middleware_error_handler.py @@ -48,9 +48,10 @@ def handle( ) returned_response = self.func(**kwargs) if returned_response is not None and isinstance(returned_response, BoltResponse): - response.status = returned_response.status # type: ignore[union-attr] - response.headers = returned_response.headers # type: ignore[union-attr] - response.body = returned_response.body # type: ignore[union-attr] + assert response is not None, "response must be provided when returning a BoltResponse from an error handler" + response.status = returned_response.status + response.headers = returned_response.headers + response.body = returned_response.body class DefaultMiddlewareErrorHandler(MiddlewareErrorHandler): diff --git a/slack_bolt/oauth/async_callback_options.py b/slack_bolt/oauth/async_callback_options.py index 88518d7e8..e1c2b2e4c 100644 --- a/slack_bolt/oauth/async_callback_options.py +++ b/slack_bolt/oauth/async_callback_options.py @@ -1,6 +1,6 @@ import logging from logging import Logger -from typing import Optional, Callable, Awaitable +from typing import Optional, Callable, Awaitable, TYPE_CHECKING from slack_sdk.oauth import RedirectUriPageRenderer, OAuthStateUtils from slack_sdk.oauth.installation_store import Installation @@ -9,6 +9,9 @@ from slack_bolt.request.async_request import AsyncBoltRequest from slack_bolt.response import BoltResponse +if TYPE_CHECKING: + from slack_bolt.oauth.async_oauth_settings import AsyncOAuthSettings + class AsyncSuccessArgs: def __init__( @@ -16,7 +19,7 @@ def __init__( *, request: AsyncBoltRequest, installation: Installation, - settings: "AsyncOAuthSettings", # type: ignore[name-defined] + settings: "AsyncOAuthSettings", default: "AsyncCallbackOptions", ): """The arguments for a success function. @@ -41,7 +44,7 @@ def __init__( reason: str, error: Optional[Exception] = None, suggested_status_code: int, - settings: "AsyncOAuthSettings", # type: ignore[name-defined] + settings: "AsyncOAuthSettings", default: "AsyncCallbackOptions", ): """The arguments for a failure function. diff --git a/slack_bolt/oauth/callback_options.py b/slack_bolt/oauth/callback_options.py index f267ed154..09584a365 100644 --- a/slack_bolt/oauth/callback_options.py +++ b/slack_bolt/oauth/callback_options.py @@ -1,6 +1,6 @@ import logging from logging import Logger -from typing import Optional, Callable +from typing import Optional, Callable, TYPE_CHECKING from slack_sdk.oauth import RedirectUriPageRenderer, OAuthStateUtils from slack_sdk.oauth.installation_store import Installation @@ -9,6 +9,9 @@ from slack_bolt.request import BoltRequest from slack_bolt.response import BoltResponse +if TYPE_CHECKING: + from slack_bolt.oauth.oauth_settings import OAuthSettings + class SuccessArgs: def __init__( @@ -16,7 +19,7 @@ def __init__( *, request: BoltRequest, installation: Installation, - settings: "OAuthSettings", # type: ignore[name-defined] + settings: "OAuthSettings", default: "CallbackOptions", ): """The arguments for a success function. @@ -41,7 +44,7 @@ def __init__( reason: str, error: Optional[Exception] = None, suggested_status_code: int, - settings: "OAuthSettings", # type: ignore[name-defined] + settings: "OAuthSettings", default: "CallbackOptions", ): """The arguments for a failure function.