Skip to content

Commit

Permalink
health: move to mypy (#1620)
Browse files Browse the repository at this point in the history
  • Loading branch information
WilliamBergamin authored Dec 29, 2024
1 parent 7b04d01 commit 0d77f8d
Show file tree
Hide file tree
Showing 68 changed files with 866 additions and 842 deletions.
23 changes: 23 additions & 0 deletions .github/workflows/mypy.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
name: mypy validation

on:
push:
branches: [main]
pull_request:

jobs:
build:
runs-on: ubuntu-latest
timeout-minutes: 5
strategy:
matrix:
python-version: ["3.13"]
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 mypy verification
run: |
./scripts/run_mypy.sh
31 changes: 0 additions & 31 deletions .github/workflows/pytype.yml

This file was deleted.

8 changes: 8 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -69,3 +69,11 @@ filterwarnings = [
"ignore:slack.* package is deprecated. Please use slack_sdk.* package instead.*:UserWarning",
]
asyncio_mode = "auto"


[tool.mypy]
files = "slack_sdk/"
exclude = ["slack_sdk/scim", "slack_sdk/rtm"]
force_union_syntax = true
warn_unused_ignores = true
enable_error_code = "ignore-without-code"
1 change: 1 addition & 0 deletions requirements/testing.txt
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,4 @@ psutil>=6.0.0,<7
boto3<=2
# For AWS tests
moto>=4.0.13,<6
mypy<=1.13.0
13 changes: 13 additions & 0 deletions scripts/run_mypy.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
#!/bin/bash
# ./scripts/run_mypy.sh

set -e

script_dir=$(dirname $0)
cd ${script_dir}/..

pip install -U pip setuptools wheel
pip install -r requirements/testing.txt \
-r requirements/optional.txt

mypy --config-file pyproject.toml
17 changes: 9 additions & 8 deletions slack_sdk/audit_logs/v1/async_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
Refer to https://slack.dev/python-slack-sdk/audit-logs/ for details.
"""

import json
import logging
from ssl import SSLContext
Expand Down Expand Up @@ -225,7 +226,7 @@ async def _perform_http_request(
headers: Dict[str, str],
) -> AuditLogsResponse:
if body_params is not None:
body_params = json.dumps(body_params)
body_params = json.dumps(body_params) # type: ignore[assignment]
headers["Content-Type"] = "application/json;charset=utf-8"

session: Optional[ClientSession] = None
Expand All @@ -252,7 +253,7 @@ async def _perform_http_request(
retry_request = RetryHttpRequest(
method=http_verb,
url=url,
headers=headers,
headers=headers, # type: ignore[arg-type]
body_params=body_params,
)

Expand All @@ -278,19 +279,19 @@ async def _perform_http_request(
)

try:
async with session.request(http_verb, url, **request_kwargs) as res:
async with session.request(http_verb, url, **request_kwargs) as res: # type: ignore[arg-type, union-attr] # noqa: E501
try:
response_body = await res.text()
retry_response = RetryHttpResponse(
status_code=res.status,
headers=res.headers,
headers=res.headers, # type: ignore[arg-type]
data=response_body.encode("utf-8") if response_body is not None else None,
)
except aiohttp.ContentTypeError:
self.logger.debug(f"No response data returned from the following API call: {url}.")
retry_response = RetryHttpResponse(
status_code=res.status,
headers=res.headers,
headers=res.headers, # type: ignore[arg-type]
)
except json.decoder.JSONDecodeError as e:
message = f"Failed to parse the response body: {str(e)}"
Expand Down Expand Up @@ -320,7 +321,7 @@ async def _perform_http_request(
url=url,
status_code=res.status,
raw_body=response_body,
headers=res.headers,
headers=res.headers, # type: ignore[arg-type]
)
_debug_log_response(self.logger, resp)
return resp
Expand Down Expand Up @@ -351,10 +352,10 @@ async def _perform_http_request(

if resp is not None:
return resp
raise last_error
raise last_error # type: ignore[misc]

finally:
if not use_running_session:
await session.close()
await session.close() # type: ignore[union-attr]

return resp
16 changes: 8 additions & 8 deletions slack_sdk/audit_logs/v1/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
Refer to https://slack.dev/python-slack-sdk/audit-logs/ for details.
"""

import json
import logging
import urllib
Expand Down Expand Up @@ -217,7 +218,7 @@ def _perform_http_request(
headers: Dict[str, str],
) -> AuditLogsResponse:
if body is not None:
body = json.dumps(body)
body = json.dumps(body) # type: ignore[assignment]
headers["Content-Type"] = "application/json;charset=utf-8"

if self.logger.level <= logging.DEBUG:
Expand All @@ -229,7 +230,7 @@ def _perform_http_request(
req = Request(
method=http_verb,
url=url,
data=body.encode("utf-8") if body is not None else None,
data=body.encode("utf-8") if body is not None else None, # type: ignore[attr-defined]
headers=headers,
)
resp = None
Expand Down Expand Up @@ -327,7 +328,7 @@ def _perform_http_request(

if resp is not None:
return resp
raise last_error
raise last_error # type: ignore[misc]

def _perform_http_request_internal(self, url: str, req: Request) -> AuditLogsResponse:
opener: Optional[OpenerDirector] = None
Expand All @@ -344,19 +345,18 @@ def _perform_http_request_internal(self, url: str, req: Request) -> AuditLogsRes
else:
raise SlackRequestError(f"Invalid URL detected: {url}")

# NOTE: BAN-B310 is already checked above
http_resp: Optional[HTTPResponse] = None
http_resp: HTTPResponse
if opener:
http_resp = opener.open(req, timeout=self.timeout) # skipcq: BAN-B310
http_resp = opener.open(req, timeout=self.timeout)
else:
http_resp = urlopen(req, context=self.ssl, timeout=self.timeout) # skipcq: BAN-B310
http_resp = urlopen(req, context=self.ssl, timeout=self.timeout)
charset: str = http_resp.headers.get_content_charset() or "utf-8"
response_body: str = http_resp.read().decode(charset)
resp = AuditLogsResponse(
url=url,
status_code=http_resp.status,
raw_body=response_body,
headers=http_resp.headers,
headers=http_resp.headers, # type: ignore[arg-type]
)
_debug_log_response(self.logger, resp)
return resp
12 changes: 6 additions & 6 deletions slack_sdk/audit_logs/v1/logs.py
Original file line number Diff line number Diff line change
Expand Up @@ -804,7 +804,7 @@ def __init__(
self.attributes = []
for a in attributes:
if isinstance(a, dict):
self.attributes.append(Attribute(**a))
self.attributes.append(Attribute(**a)) # type: ignore[arg-type]
else:
self.attributes.append(a)
self.channel = channel
Expand All @@ -822,11 +822,11 @@ def __init__(
self.rules_checked = None
if rules_checked is not None:
self.rules_checked = []
for a in rules_checked:
for a in rules_checked: # type: ignore[assignment]
if isinstance(a, dict):
self.rules_checked.append(AAARule(**a))
self.rules_checked.append(AAARule(**a)) # type: ignore[arg-type]
else:
self.rules_checked.append(a)
self.rules_checked.append(a) # type: ignore[arg-type]
self.disconnecting_team = disconnecting_team
self.is_channel_canvas = is_channel_canvas
self.linked_channel_id = linked_channel_id
Expand Down Expand Up @@ -1021,7 +1021,7 @@ def __init__(
class WorkflowV2StepConfiguration:
name: Optional[str]
step_function_type: Optional[str]
step_function_app_id: Optional[int]
step_function_app_id: Optional[str]
unknown_fields: Dict[str, Any]

def __init__(
Expand Down Expand Up @@ -1235,7 +1235,7 @@ def __init__(
provided: Optional[str] = None,
**kwargs,
) -> None:
self.entries = [Entry(**e) if isinstance(e, dict) else e for e in entries]
self.entries = [Entry(**e) if isinstance(e, dict) else e for e in entries] # type: ignore[union-attr]
self.response_metadata = (
ResponseMetadata(**response_metadata) if isinstance(response_metadata, dict) else response_metadata
)
Expand Down
4 changes: 2 additions & 2 deletions slack_sdk/audit_logs/v1/response.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ class AuditLogsResponse:
body: Optional[Dict[str, Any]]
typed_body: Optional[LogsResponse]

@property
def typed_body(self) -> Optional[LogsResponse]: # type: ignore
@property # type: ignore[no-redef]
def typed_body(self) -> Optional[LogsResponse]:
if self.body is None:
return None
return LogsResponse(**self.body)
Expand Down
6 changes: 3 additions & 3 deletions slack_sdk/http_retry/builtin_async_handlers.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ async def prepare_for_next_attempt_async(
error: Optional[Exception] = None,
) -> None:
if response is None:
raise error
raise error # type: ignore[misc]

state.next_attempt_requested = True
retry_after_header_name: Optional[str] = None
Expand All @@ -79,9 +79,9 @@ async def prepare_for_next_attempt_async(
duration = 1
if retry_after_header_name is None:
# This situation usually does not arise. Just in case.
duration += random.random()
duration += random.random() # type: ignore[assignment]
else:
duration = int(response.headers.get(retry_after_header_name)[0]) + random.random()
duration = int(response.headers.get(retry_after_header_name)[0]) + random.random() # type: ignore[assignment, index] # noqa: E501
await asyncio.sleep(duration)
state.increment_current_attempt()

Expand Down
6 changes: 3 additions & 3 deletions slack_sdk/http_retry/builtin_handlers.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ def prepare_for_next_attempt(
error: Optional[Exception] = None,
) -> None:
if response is None:
raise error
raise error # type: ignore[misc]

state.next_attempt_requested = True
retry_after_header_name: Optional[str] = None
Expand All @@ -82,9 +82,9 @@ def prepare_for_next_attempt(
duration = 1
if retry_after_header_name is None:
# This situation usually does not arise. Just in case.
duration += random.random()
duration += random.random() # type: ignore[assignment]
else:
duration = int(response.headers.get(retry_after_header_name)[0]) + random.random()
duration = int(response.headers.get(retry_after_header_name)[0]) + random.random() # type: ignore[index, assignment] # noqa: E501
time.sleep(duration)
state.increment_current_attempt()

Expand Down
4 changes: 2 additions & 2 deletions slack_sdk/http_retry/request.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,8 @@ def __init__(
@classmethod
def from_urllib_http_request(cls, req: Request) -> "HttpRequest":
return HttpRequest(
method=req.method,
method=req.method, # type: ignore[arg-type]
url=req.full_url,
headers={k: v if isinstance(v, list) else [v] for k, v in req.headers.items()},
data=req.data,
data=req.data, # type: ignore[arg-type]
)
2 changes: 1 addition & 1 deletion slack_sdk/http_retry/response.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ class HttpResponse:
"""HTTP response representation"""

status_code: int
headers: Dict[str, List[str]]
headers: Dict[str, Union[List[str], str]]
body: Optional[Dict[str, Any]]
data: Optional[bytes]

Expand Down
11 changes: 5 additions & 6 deletions slack_sdk/models/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
# NOTE: used only for legacy components - don't use this for Block Kit
def extract_json(
item_or_items: Union[JsonObject, Sequence[JsonObject]], *format_args
) -> Union[Dict[Any, Any], List[Dict[Any, Any]]]: # type: ignore
) -> Union[Dict[Any, Any], List[Dict[Any, Any]], Sequence[JsonObject]]:
"""
Given a sequence (or single item), attempt to call the to_dict() method on each
item and return a plain list. If item is not the expected type, return it
Expand All @@ -24,13 +24,12 @@ def extract_json(
method
"""
try:
return [ # type: ignore
elem.to_dict(*format_args) if isinstance(elem, JsonObject) else elem for elem in item_or_items
return [
elem.to_dict(*format_args) if isinstance(elem, JsonObject) else elem
for elem in item_or_items # type: ignore[union-attr]
]
except TypeError: # not iterable, so try returning it as a single item
return ( # type: ignore
item_or_items.to_dict(*format_args) if isinstance(item_or_items, JsonObject) else item_or_items
)
return item_or_items.to_dict(*format_args) if isinstance(item_or_items, JsonObject) else item_or_items


def show_unknown_key_warning(name: Union[str, object], others: dict):
Expand Down
8 changes: 4 additions & 4 deletions slack_sdk/models/attachments/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ def __init__(
def name_or_url_present(self):
return self.name is not None or self.url is not None

def to_dict(self) -> dict: # skipcq: PYL-W0221
def to_dict(self) -> dict:
json = super().to_dict()
json["type"] = self.subtype
return json
Expand Down Expand Up @@ -212,7 +212,7 @@ class ActionExternalSelector(AbstractActionSelector):
data_source = "external"

@property
def attributes(self) -> Set[str]:
def attributes(self) -> Set[str]: # type: ignore[override]
return super().attributes.union({"min_query_length"})

def __init__(
Expand Down Expand Up @@ -417,7 +417,7 @@ def author_link_without_author_name(self) -> bool:
def author_link_without_author_icon(self) -> bool:
return self.author_link is None or self.author_icon is not None

def to_dict(self) -> dict: # skipcq: PYL-W0221
def to_dict(self) -> dict:
json = super().to_dict()
if self.fields is not None:
json["fields"] = extract_json(self.fields)
Expand Down Expand Up @@ -469,7 +469,7 @@ def to_dict(self) -> dict:

class InteractiveAttachment(Attachment):
@property
def attributes(self) -> Set[str]:
def attributes(self) -> Set[str]: # type: ignore[override]
return super().attributes.union({"callback_id"})

actions_max_length = 5
Expand Down
Loading

0 comments on commit 0d77f8d

Please sign in to comment.