Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

health: move to mypy #1130

Merged
merged 10 commits into from
Aug 21, 2024
Merged
8 changes: 4 additions & 4 deletions .github/workflows/pytype.yml → .github/workflows/mypy.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: Run pytype validation
name: Run mypy validation

on:
push:
Expand All @@ -11,13 +11,13 @@ jobs:
timeout-minutes: 20
strategy:
matrix:
python-version: ["3.9"]
python-version: ["3.12"]
steps:
- uses: actions/checkout@v4
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
- name: Run pytype verification
- name: Run mypy verification
run: |
./scripts/run_pytype.sh
./scripts/run_mypy.sh
1 change: 0 additions & 1 deletion requirements/testing_without_asyncio.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
# pip install -r requirements/testing_without_asyncio.txt
pytest>=6.2.5,<8.4 # https://github.com/tornadoweb/tornado/issues/3375
pytest-cov>=3,<6
black==22.8.0 # Until we drop Python 3.6 support, we have to stay with this version
3 changes: 3 additions & 0 deletions requirements/tools.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
mypy==1.11.1
flake8==6.0.0
black==22.8.0 # Until we drop Python 3.6 support, we have to stay with this version
9 changes: 7 additions & 2 deletions scripts/install_all_and_run_tests.sh
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@
script_dir=`dirname $0`
cd ${script_dir}/..
rm -rf ./slack_bolt.egg-info

# Update pip to prevent warnings
pip install -U pip

# The package causes a conflict with moto
pip uninstall python-lambda

Expand All @@ -32,10 +36,11 @@ else
pip install -U -r requirements/testing.txt && \
pip install -U -r requirements/adapter.txt && \
pip install -U -r requirements/adapter_testing.txt && \
pip install -r requirements/tools.txt && \
# To avoid errors due to the old versions of click forced by Chalice
pip install -U pip click && \
black slack_bolt/ tests/ && \
flake8 slack_bolt/ && flake8 examples/
pytest && \
pip install "pytype==2022.12.15" && \
pytype slack_bolt/
mypy slack_bolt/
fi
2 changes: 1 addition & 1 deletion scripts/run_flake8.sh
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,5 @@

script_dir=$(dirname $0)
cd ${script_dir}/.. && \
pip install "flake8==6.0.0" && \
pip install -r requirements/tools.txt && \
flake8 slack_bolt/ && flake8 examples/
6 changes: 3 additions & 3 deletions scripts/run_pytype.sh → scripts/run_mypy.sh
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
#!/bin/bash
# ./scripts/run_pytype.sh
# ./scripts/run_mypy.sh

script_dir=$(dirname $0)
cd ${script_dir}/.. && \
pip install .
pip install -r requirements/async.txt && \
pip install -r requirements/adapter.txt && \
pip install "pytype==2022.12.15" && \
pytype slack_bolt/
pip install -r requirements/tools.txt && \
mypy slack_bolt/
11 changes: 0 additions & 11 deletions scripts/run_tests.sh
Original file line number Diff line number Diff line change
Expand Up @@ -14,17 +14,6 @@ then
black slack_bolt/ tests/ && \
pytest -vv $1
else
if [ ${python_version:0:3} == "3.8" ]
then
# pytype's behavior can be different in older Python versions
black slack_bolt/ tests/ \
&& pytest -vv \
&& pip install -r requirements/adapter.txt \
&& pip install -r requirements/adapter_testing.txt \
&& pip install -U pip setuptools wheel \
&& pip install -U pytype \
&& pytype slack_bolt/
else
black slack_bolt/ tests/ && pytest
fi
fi
4 changes: 2 additions & 2 deletions slack_bolt/adapter/aiohttp/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ async def to_bolt_request(request: web.Request) -> AsyncBoltRequest:
return AsyncBoltRequest(
body=await request.text(),
query=request.query_string,
headers=request.headers,
headers=request.headers, # type: ignore[arg-type]
)


Expand All @@ -33,7 +33,7 @@ async def to_aiohttp_response(bolt_resp: BoltResponse) -> web.Response:
value=c.value,
max_age=c.get("max-age"),
expires=c.get("expires"),
path=c.get("path"),
path=c.get("path"), # type: ignore[arg-type]
domain=c.get("domain"),
secure=True,
httponly=True,
Expand Down
8 changes: 2 additions & 6 deletions slack_bolt/adapter/asgi/aiohttp/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
from slack_bolt.oauth.async_oauth_flow import AsyncOAuthFlow

from slack_bolt.adapter.asgi.http_request import AsgiHttpRequest
from slack_bolt.adapter.asgi.builtin import SlackRequestHandler

Expand Down Expand Up @@ -41,13 +39,11 @@
)

async def handle_installation(self, request: AsgiHttpRequest) -> BoltResponse:
oauth_flow: AsyncOAuthFlow = self.app.oauth_flow
return await oauth_flow.handle_installation(
return await self.app.oauth_flow.handle_installation( # type: ignore[union-attr]
AsyncBoltRequest(body=await request.get_raw_body(), query=request.query_string, headers=request.get_headers())
)

async def handle_callback(self, request: AsgiHttpRequest) -> BoltResponse:
oauth_flow: AsyncOAuthFlow = self.app.oauth_flow
return await oauth_flow.handle_callback(
return await self.app.oauth_flow.handle_callback( # type: ignore[union-attr]

Check warning on line 47 in slack_bolt/adapter/asgi/aiohttp/__init__.py

View check run for this annotation

Codecov / codecov/patch

slack_bolt/adapter/asgi/aiohttp/__init__.py#L47

Added line #L47 was not covered by tests
AsyncBoltRequest(body=await request.get_raw_body(), query=request.query_string, headers=request.get_headers())
)
14 changes: 7 additions & 7 deletions slack_bolt/adapter/asgi/base_handler.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from typing import Callable, Dict
from typing import Callable, Dict, Union

from .http_request import AsgiHttpRequest
from .http_response import AsgiHttpResponse
Expand All @@ -14,7 +14,7 @@


class BaseSlackRequestHandler:
app: App # type: ignore
app: Union[App, "AsyncApp"] # type: ignore[name-defined]
path: str

async def dispatch(self, request: AsgiHttpRequest) -> BoltResponse:
Expand All @@ -37,13 +37,13 @@
return AsgiHttpResponse(
status=bolt_response.status, headers=bolt_response.headers, body=bolt_response.body
)
if path == self.app.oauth_flow.redirect_uri_path:
bolt_response: BoltResponse = await self.handle_callback(request)
elif path == self.app.oauth_flow.redirect_uri_path:
bolt_response = await self.handle_callback(request)

Check warning on line 41 in slack_bolt/adapter/asgi/base_handler.py

View check run for this annotation

Codecov / codecov/patch

slack_bolt/adapter/asgi/base_handler.py#L40-L41

Added lines #L40 - L41 were not covered by tests
return AsgiHttpResponse(
status=bolt_response.status, headers=bolt_response.headers, body=bolt_response.body
)
if method == "POST" and path == self.path:
bolt_response: BoltResponse = await self.dispatch(request)
bolt_response = await self.dispatch(request)
return AsgiHttpResponse(status=bolt_response.status, headers=bolt_response.headers, body=bolt_response.body)
return AsgiHttpResponse(status=404, headers={"content-type": ["text/plain;charset=utf-8"]}, body="Not Found")

Expand All @@ -60,12 +60,12 @@
async def __call__(self, scope: scope_type, receive: Callable, send: Callable) -> None:
if scope["type"] == "http":
response: AsgiHttpResponse = await self._get_http_response(
scope["method"], scope["path"], AsgiHttpRequest(scope, receive)
method=scope["method"], path=scope["path"], request=AsgiHttpRequest(scope, receive) # type: ignore[arg-type]
)
await send(response.get_response_start())
await send(response.get_response_body())
return
if scope["type"] == "lifespan":
await send(await self._handle_lifespan(receive))
return
raise TypeError(f"Unsupported scope type: {scope['type']}")
raise TypeError(f"Unsupported scope type: {scope['type']!r}")
7 changes: 2 additions & 5 deletions slack_bolt/adapter/asgi/builtin/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
from slack_bolt.oauth.oauth_flow import OAuthFlow
from slack_bolt.adapter.asgi.http_request import AsgiHttpRequest

from slack_bolt import App
Expand Down Expand Up @@ -39,13 +38,11 @@
)

async def handle_installation(self, request: AsgiHttpRequest) -> BoltResponse:
oauth_flow: OAuthFlow = self.app.oauth_flow
return oauth_flow.handle_installation(
return self.app.oauth_flow.handle_installation( # type: ignore[union-attr]
BoltRequest(body=await request.get_raw_body(), query=request.query_string, headers=request.get_headers())
)

async def handle_callback(self, request: AsgiHttpRequest) -> BoltResponse:
oauth_flow: OAuthFlow = self.app.oauth_flow
return oauth_flow.handle_callback(
return self.app.oauth_flow.handle_callback( # type: ignore[union-attr]

Check warning on line 46 in slack_bolt/adapter/asgi/builtin/__init__.py

View check run for this annotation

Codecov / codecov/patch

slack_bolt/adapter/asgi/builtin/__init__.py#L46

Added line #L46 was not covered by tests
BoltRequest(body=await request.get_raw_body(), query=request.query_string, headers=request.get_headers())
)
10 changes: 5 additions & 5 deletions slack_bolt/adapter/asgi/http_request.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from typing import Callable, Dict, Union
from typing import Callable, Dict, Iterable, Sequence, Tuple, Union

from .utils import scope_type, ENCODING

Expand All @@ -8,10 +8,10 @@ class AsgiHttpRequest:

def __init__(self, scope: scope_type, receive: Callable):
self.receive = receive
self.query_string = str(scope["query_string"], ENCODING)
self.raw_headers = scope["headers"]
self.query_string = str(scope["query_string"], ENCODING) # type: ignore[arg-type]
self.raw_headers: Iterable[Tuple[bytes, bytes]] = scope["headers"] # type: ignore[assignment]

def get_headers(self) -> Dict[str, str]:
def get_headers(self) -> Dict[str, Union[str, Sequence[str]]]:
return {str(header[0], ENCODING): str(header[1], (ENCODING)) for header in self.raw_headers}

async def get_raw_body(self) -> str:
Expand All @@ -22,7 +22,7 @@ async def get_raw_body(self) -> str:
if chunk["type"] != "http.request":
raise Exception("Body chunks could not be received from asgi server")

chunks.extend(chunk.get("body", b""))
chunks.extend(chunk.get("body", b"")) # type: ignore[arg-type]
if not chunk.get("more_body", False):
break
return bytes(chunks).decode(ENCODING)
16 changes: 8 additions & 8 deletions slack_bolt/adapter/aws_lambda/chalice_handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
from os import getenv
from typing import Optional

from botocore.client import BaseClient
from botocore.client import BaseClient # type: ignore[import-untyped]

from chalice.app import Request, Response, Chalice

Expand All @@ -29,7 +29,7 @@ def __init__(self, app: App, chalice: Chalice, lambda_client: Optional[BaseClien
LocalLambdaClient,
)

lambda_client = LocalLambdaClient(self.chalice, None)
lambda_client = LocalLambdaClient(self.chalice, None) # type: ignore[arg-type]
except ImportError:
logging.info("Failed to load LocalLambdaClient for CLI mode.")
pass
Expand All @@ -50,7 +50,7 @@ def clear_all_log_handlers(cls):
root.removeHandler(handler)

def handle(self, request: Request):
body: str = request.raw_body.decode("utf-8") if request.raw_body else ""
body: str = request.raw_body.decode("utf-8") if request.raw_body else "" # type: ignore[union-attr]
self.logger.debug(f"Incoming request: {request.to_dict()}, body: {body}")

method = request.method
Expand All @@ -72,7 +72,7 @@ def handle(self, request: Request):
bolt_resp = oauth_flow.handle_installation(bolt_req)
return to_chalice_response(bolt_resp)
elif method == "POST":
bolt_req: BoltRequest = to_bolt_request(request, body)
bolt_req = to_bolt_request(request, body)
# https://docs.aws.amazon.com/lambda/latest/dg/python-context.html
aws_lambda_function_name = self.chalice.lambda_context.function_name
bolt_req.context["aws_lambda_function_name"] = aws_lambda_function_name
Expand All @@ -81,7 +81,7 @@ def handle(self, request: Request):
aws_response = to_chalice_response(bolt_resp)
return aws_response
elif method == "NONE":
bolt_req: BoltRequest = to_bolt_request(request, body)
bolt_req = to_bolt_request(request, body)
bolt_resp = self.app.dispatch(bolt_req)
aws_response = to_chalice_response(bolt_resp)
return aws_response
Expand All @@ -92,16 +92,16 @@ def handle(self, request: Request):
def to_bolt_request(request: Request, body: str) -> BoltRequest:
return BoltRequest(
body=body,
query=request.query_params,
headers=request.headers,
query=request.query_params, # type: ignore[arg-type]
headers=request.headers, # type: ignore[arg-type]
)


def to_chalice_response(resp: BoltResponse) -> Response:
return Response(
status_code=resp.status,
body=resp.body,
headers=resp.first_headers(),
headers=resp.first_headers(), # type: ignore[arg-type]
)


Expand Down
6 changes: 3 additions & 3 deletions slack_bolt/adapter/aws_lambda/chalice_lazy_listener_runner.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
from logging import Logger
from typing import Callable, Optional

import boto3
from botocore.client import BaseClient
import boto3 # type: ignore[import-untyped]
from botocore.client import BaseClient # type: ignore[import-untyped]

from slack_bolt import BoltRequest
from slack_bolt.lazy_listener import LazyListenerRunner
Expand All @@ -20,7 +20,7 @@ def start(self, function: Callable[..., None], request: BoltRequest) -> None:

chalice_request: dict = request.context["chalice_request"]
request.headers["x-slack-bolt-lazy-only"] = ["1"]
request.headers["x-slack-bolt-lazy-function-name"] = [request.lazy_function_name]
request.headers["x-slack-bolt-lazy-function-name"] = [request.lazy_function_name] # type: ignore[list-item]
payload = {
"method": "NONE",
"headers": {k: v[0] for k, v in request.headers.items()},
Expand Down
2 changes: 1 addition & 1 deletion slack_bolt/adapter/aws_lambda/handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@


class SlackRequestHandler:
def __init__(self, app: App): # type: ignore
def __init__(self, app: App):
self.app = app
self.logger = get_bolt_app_logger(app.name, SlackRequestHandler, app.logger)
self.app.listener_runner.lazy_listener_runner = LambdaLazyListenerRunner(self.logger)
Expand Down
2 changes: 1 addition & 1 deletion slack_bolt/adapter/aws_lambda/lambda_s3_oauth_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
from logging import Logger
from typing import Optional

import boto3
import boto3 # type: ignore[import-untyped]

from slack_bolt.authorization.authorize import InstallationStoreAuthorize
from slack_bolt.oauth import OAuthFlow
Expand Down
2 changes: 1 addition & 1 deletion slack_bolt/adapter/aws_lambda/lazy_listener_runner.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
from logging import Logger
from typing import Callable, Optional, Any

import boto3
import boto3 # type: ignore[import-untyped]

from slack_bolt import BoltRequest
from slack_bolt.lazy_listener import LazyListenerRunner
Expand Down
6 changes: 3 additions & 3 deletions slack_bolt/adapter/bottle/handler.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from bottle import Request, Response
from bottle import Request, Response # type: ignore[import-untyped]

from slack_bolt.app import App
from slack_bolt.oauth import OAuthFlow
Expand All @@ -25,7 +25,7 @@ def set_response(bolt_resp: BoltResponse, resp: Response) -> None:


class SlackRequestHandler:
def __init__(self, app: App): # type: ignore
def __init__(self, app: App):
self.app = app

def handle(self, req: Request, resp: Response) -> str:
Expand All @@ -41,7 +41,7 @@ def handle(self, req: Request, resp: Response) -> str:
set_response(bolt_resp, resp)
return bolt_resp.body or ""
elif req.method == "POST":
bolt_resp: BoltResponse = self.app.dispatch(to_bolt_request(req))
bolt_resp = self.app.dispatch(to_bolt_request(req))
set_response(bolt_resp, resp)
return bolt_resp.body or ""

Expand Down
6 changes: 3 additions & 3 deletions slack_bolt/adapter/cherrypy/handler.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from typing import Optional

import cherrypy
import cherrypy # type: ignore[import-untyped]

from slack_bolt.app import App
from slack_bolt.oauth import OAuthFlow
Expand Down Expand Up @@ -55,7 +55,7 @@ def slack_processor(entity):


class SlackRequestHandler:
def __init__(self, app: App): # type: ignore
def __init__(self, app: App):
self.app = app

def handle(self) -> bytes:
Expand All @@ -73,7 +73,7 @@ def handle(self) -> bytes:
set_response_status_and_headers(bolt_resp)
return (bolt_resp.body or "").encode("utf-8")
elif req.method == "POST":
bolt_resp: BoltResponse = self.app.dispatch(build_bolt_request())
bolt_resp = self.app.dispatch(build_bolt_request())
set_response_status_and_headers(bolt_resp)
return (bolt_resp.body or "").encode("utf-8")

Expand Down
Loading