Skip to content

Commit 77e906a

Browse files
authored
Enable pytype and fix detected issues (#1128)
1 parent db6621c commit 77e906a

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

44 files changed

+428
-314
lines changed

.github/workflows/pytype.yml

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
name: pytype validation
2+
3+
on:
4+
push:
5+
branches: [ main ]
6+
pull_request:
7+
8+
jobs:
9+
build:
10+
runs-on: ubuntu-latest
11+
timeout-minutes: 20
12+
strategy:
13+
matrix:
14+
python-version: ['3.9']
15+
steps:
16+
- uses: actions/checkout@v2
17+
- name: Set up Python ${{ matrix.python-version }}
18+
uses: actions/setup-python@v2
19+
with:
20+
python-version: ${{ matrix.python-version }}
21+
- name: Install dependencies
22+
run: |
23+
pip install -U pip
24+
pip install -e ".[testing]"
25+
pip install -e ".[optional]"
26+
# As pytype can change its behavior in newer versions, we manually upgrade it
27+
pip install "pytype==2021.10.11"
28+
- name: Run pytype
29+
run: |
30+
pytype slack_sdk/

slack_sdk/audit_logs/v1/async_client.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ def __init__(
100100
async def schemas(
101101
self,
102102
*,
103-
query_params: Optional[Dict[str, any]] = None,
103+
query_params: Optional[Dict[str, Any]] = None,
104104
headers: Optional[Dict[str, str]] = None,
105105
) -> AuditLogsResponse:
106106
"""Returns information about the kind of objects which the Audit Logs API
@@ -122,7 +122,7 @@ async def schemas(
122122
async def actions(
123123
self,
124124
*,
125-
query_params: Optional[Dict[str, any]] = None,
125+
query_params: Optional[Dict[str, Any]] = None,
126126
headers: Optional[Dict[str, str]] = None,
127127
) -> AuditLogsResponse:
128128
"""Returns information about the kind of actions that the Audit Logs API
@@ -151,7 +151,7 @@ async def logs(
151151
action: Optional[str] = None,
152152
actor: Optional[str] = None,
153153
entity: Optional[str] = None,
154-
additional_query_params: Optional[Dict[str, any]] = None,
154+
additional_query_params: Optional[Dict[str, Any]] = None,
155155
headers: Optional[Dict[str, str]] = None,
156156
) -> AuditLogsResponse:
157157
"""This is the primary endpoint for retrieving actual audit events from your organization.
@@ -199,8 +199,8 @@ async def api_call(
199199
*,
200200
http_verb: str = "GET",
201201
path: str,
202-
query_params: Optional[Dict[str, any]] = None,
203-
body_params: Optional[Dict[str, any]] = None,
202+
query_params: Optional[Dict[str, Any]] = None,
203+
body_params: Optional[Dict[str, Any]] = None,
204204
headers: Optional[Dict[str, str]] = None,
205205
) -> AuditLogsResponse:
206206
url = f"{self.base_url}{path}"

slack_sdk/audit_logs/v1/client.py

Lines changed: 25 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
import urllib
88
from http.client import HTTPResponse
99
from ssl import SSLContext
10-
from typing import Dict, Optional, List
10+
from typing import Dict, Optional, List, Any
1111
from urllib.error import HTTPError
1212
from urllib.request import Request, urlopen, OpenerDirector, ProxyHandler, HTTPSHandler
1313

@@ -89,7 +89,7 @@ def __init__(
8989
def schemas(
9090
self,
9191
*,
92-
query_params: Optional[Dict[str, any]] = None,
92+
query_params: Optional[Dict[str, Any]] = None,
9393
headers: Optional[Dict[str, str]] = None,
9494
) -> AuditLogsResponse:
9595
"""Returns information about the kind of objects which the Audit Logs API
@@ -111,7 +111,7 @@ def schemas(
111111
def actions(
112112
self,
113113
*,
114-
query_params: Optional[Dict[str, any]] = None,
114+
query_params: Optional[Dict[str, Any]] = None,
115115
headers: Optional[Dict[str, str]] = None,
116116
) -> AuditLogsResponse:
117117
"""Returns information about the kind of actions that the Audit Logs API
@@ -140,7 +140,7 @@ def logs(
140140
action: Optional[str] = None,
141141
actor: Optional[str] = None,
142142
entity: Optional[str] = None,
143-
additional_query_params: Optional[Dict[str, any]] = None,
143+
additional_query_params: Optional[Dict[str, Any]] = None,
144144
headers: Optional[Dict[str, str]] = None,
145145
) -> AuditLogsResponse:
146146
"""This is the primary endpoint for retrieving actual audit events from your organization.
@@ -188,8 +188,8 @@ def api_call(
188188
*,
189189
http_verb: str = "GET",
190190
path: str,
191-
query_params: Optional[Dict[str, any]] = None,
192-
body_params: Optional[Dict[str, any]] = None,
191+
query_params: Optional[Dict[str, Any]] = None,
192+
body_params: Optional[Dict[str, Any]] = None,
193193
headers: Optional[Dict[str, str]] = None,
194194
) -> AuditLogsResponse:
195195
"""Performs a Slack API request and returns the result."""
@@ -214,7 +214,7 @@ def _perform_http_request(
214214
*,
215215
http_verb: str = "GET",
216216
url: str,
217-
body: Optional[Dict[str, any]] = None,
217+
body: Optional[Dict[str, Any]] = None,
218218
headers: Dict[str, str],
219219
) -> AuditLogsResponse:
220220
if body is not None:
@@ -261,11 +261,20 @@ def _perform_http_request(
261261
url=url,
262262
status_code=e.code,
263263
raw_body=response_body,
264-
headers=e.headers,
264+
headers=dict(e.headers.items()),
265265
)
266266
if e.code == 429:
267267
# for backward-compatibility with WebClient (v.2.5.0 or older)
268-
resp.headers["Retry-After"] = resp.headers["retry-after"]
268+
if (
269+
"retry-after" not in resp.headers
270+
and "Retry-After" in resp.headers
271+
):
272+
resp.headers["retry-after"] = resp.headers["Retry-After"]
273+
if (
274+
"Retry-After" not in resp.headers
275+
and "retry-after" in resp.headers
276+
):
277+
resp.headers["Retry-After"] = resp.headers["retry-after"]
269278
_debug_log_response(self.logger, resp)
270279

271280
# Try to find a retry handler for this error
@@ -356,20 +365,20 @@ def _perform_http_request_internal(
356365
raise SlackRequestError(f"Invalid URL detected: {url}")
357366

358367
# NOTE: BAN-B310 is already checked above
359-
resp: Optional[HTTPResponse] = None
368+
http_resp: Optional[HTTPResponse] = None
360369
if opener:
361-
resp = opener.open(req, timeout=self.timeout) # skipcq: BAN-B310
370+
http_resp = opener.open(req, timeout=self.timeout) # skipcq: BAN-B310
362371
else:
363-
resp = urlopen( # skipcq: BAN-B310
372+
http_resp = urlopen( # skipcq: BAN-B310
364373
req, context=self.ssl, timeout=self.timeout
365374
)
366-
charset: str = resp.headers.get_content_charset() or "utf-8"
367-
response_body: str = resp.read().decode(charset)
375+
charset: str = http_resp.headers.get_content_charset() or "utf-8"
376+
response_body: str = http_resp.read().decode(charset)
368377
resp = AuditLogsResponse(
369378
url=url,
370-
status_code=resp.status,
379+
status_code=http_resp.status,
371380
raw_body=response_body,
372-
headers=resp.headers,
381+
headers=http_resp.headers,
373382
)
374383
_debug_log_response(self.logger, resp)
375384
return resp

slack_sdk/audit_logs/v1/response.py

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import json
2-
from typing import Dict, Any
2+
from typing import Dict, Any, Optional
33

44
from slack_sdk.audit_logs.v1.logs import LogsResponse
55

@@ -8,20 +8,22 @@ class AuditLogsResponse:
88
url: str
99
status_code: int
1010
headers: Dict[str, Any]
11-
raw_body: str
12-
body: Dict[str, Any]
13-
typed_body: LogsResponse
11+
raw_body: Optional[str]
12+
body: Optional[Dict[str, Any]]
13+
typed_body: Optional[LogsResponse]
1414

1515
@property
16-
def typed_body(self) -> LogsResponse:
16+
def typed_body(self) -> Optional[LogsResponse]: # type: ignore
17+
if self.body is None:
18+
return None
1719
return LogsResponse(**self.body)
1820

1921
def __init__(
2022
self,
2123
*,
2224
url: str,
2325
status_code: int,
24-
raw_body: str,
26+
raw_body: Optional[str],
2527
headers: dict,
2628
):
2729
self.url = url

slack_sdk/http_retry/builtin_async_handlers.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import asyncio
22
import random
3-
from typing import Optional, List
3+
from typing import Optional, List, Type
44

55
from aiohttp import ServerDisconnectedError, ServerConnectionError, ClientOSError
66

@@ -19,7 +19,7 @@ def __init__(
1919
self,
2020
max_retry_count: int = 1,
2121
interval_calculator: RetryIntervalCalculator = default_interval_calculator,
22-
error_types: List[Exception] = [
22+
error_types: List[Type[Exception]] = [
2323
ServerConnectionError,
2424
ServerDisconnectedError,
2525
# ClientOSError: [Errno 104] Connection reset by peer
@@ -57,7 +57,7 @@ async def _can_retry_async(
5757
response: Optional[HttpResponse],
5858
error: Optional[Exception],
5959
) -> bool:
60-
return response.status_code == 429
60+
return response is not None and response.status_code == 429
6161

6262
async def prepare_for_next_attempt_async(
6363
self,

slack_sdk/http_retry/builtin_handlers.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import random
22
import time
33
from http.client import RemoteDisconnected
4-
from typing import Optional, List
4+
from typing import Optional, List, Type
55
from urllib.error import URLError
66

77
from slack_sdk.http_retry.interval_calculator import RetryIntervalCalculator
@@ -18,7 +18,7 @@ def __init__(
1818
self,
1919
max_retry_count: int = 1,
2020
interval_calculator: RetryIntervalCalculator = default_interval_calculator,
21-
error_types: List[Exception] = [
21+
error_types: List[Type[Exception]] = [
2222
# To cover URLError: <urlopen error [Errno 104] Connection reset by peer>
2323
URLError,
2424
ConnectionResetError,

slack_sdk/models/__init__.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
# NOTE: used only for legacy components - don't use this for Block Kit
1313
def extract_json(
1414
item_or_items: Union[JsonObject, Sequence[JsonObject]], *format_args
15-
) -> Union[Dict[Any, Any], List[Dict[Any, Any]]]:
15+
) -> Union[Dict[Any, Any], List[Dict[Any, Any]]]: # type: ignore
1616
"""
1717
Given a sequence (or single item), attempt to call the to_dict() method on each
1818
item and return a plain list. If item is not the expected type, return it
@@ -24,12 +24,12 @@ def extract_json(
2424
method
2525
"""
2626
try:
27-
return [
27+
return [ # type: ignore
2828
elem.to_dict(*format_args) if isinstance(elem, JsonObject) else elem
2929
for elem in item_or_items
3030
]
3131
except TypeError: # not iterable, so try returning it as a single item
32-
return (
32+
return ( # type: ignore
3333
item_or_items.to_dict(*format_args)
3434
if isinstance(item_or_items, JsonObject)
3535
else item_or_items

slack_sdk/models/attachments/__init__.py

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -284,8 +284,7 @@ class Attachment(JsonObject):
284284
"ts",
285285
}
286286

287-
fields: List[AttachmentField]
288-
actions: List[Action]
287+
fields: Sequence[AttachmentField]
289288

290289
MarkdownFields = {"fields", "pretext", "text"}
291290

@@ -388,11 +387,11 @@ def __init__(
388387
self.markdown_in = markdown_in or []
389388

390389
@JsonValidator(f"footer attribute cannot exceed {footer_max_length} characters")
391-
def footer_length(self):
390+
def footer_length(self) -> bool:
392391
return self.footer is None or len(self.footer) <= self.footer_max_length
393392

394393
@JsonValidator("ts attribute cannot be present if footer attribute is absent")
395-
def ts_without_footer(self):
394+
def ts_without_footer(self) -> bool:
396395
return self.ts is None or self.footer is not None
397396

398397
@EnumValidator("markdown_in", MarkdownFields)
@@ -404,23 +403,23 @@ def markdown_in_valid(self):
404403
@JsonValidator(
405404
"color attribute must be 'good', 'warning', 'danger', or a hex color code"
406405
)
407-
def color_valid(self):
406+
def color_valid(self) -> bool:
408407
return (
409408
self.color is None
410409
or self.color in SeededColors
411-
or re.match("^#(?:[0-9A-F]{2}){3}$", self.color, re.IGNORECASE)
410+
or re.match("^#(?:[0-9A-F]{2}){3}$", self.color, re.IGNORECASE) is not None
412411
)
413412

414413
@JsonValidator("image_url attribute cannot be present if thumb_url is populated")
415-
def image_url_and_thumb_url_populated(self):
414+
def image_url_and_thumb_url_populated(self) -> bool:
416415
return self.image_url is None or self.thumb_url is None
417416

418417
@JsonValidator("name must be present if link is present")
419-
def author_link_without_author_name(self):
418+
def author_link_without_author_name(self) -> bool:
420419
return self.author_link is None or self.author_name is not None
421420

422421
@JsonValidator("icon must be present if link is present")
423-
def author_link_without_author_icon(self):
422+
def author_link_without_author_icon(self) -> bool:
424423
return self.author_link is None or self.author_icon is not None
425424

426425
def to_dict(self) -> dict: # skipcq: PYL-W0221
@@ -463,7 +462,7 @@ def __init__(
463462
self.blocks = list(blocks)
464463

465464
@JsonValidator("fields attribute cannot be populated on BlockAttachment")
466-
def fields_attribute_absent(self):
465+
def fields_attribute_absent(self) -> bool:
467466
return not self.fields
468467

469468
def to_dict(self) -> dict:
@@ -588,7 +587,7 @@ def __init__(
588587
self.actions = actions or []
589588

590589
@JsonValidator(f"actions attribute cannot exceed {actions_max_length} elements")
591-
def actions_length(self):
590+
def actions_length(self) -> bool:
592591
return len(self.actions) <= self.actions_max_length
593592

594593
def to_dict(self) -> dict:

slack_sdk/models/blocks/basic_components.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -417,7 +417,7 @@ def __init__(
417417
text: Union[str, dict, TextObject],
418418
confirm: Union[str, dict, PlainTextObject] = "Yes",
419419
deny: Union[str, dict, PlainTextObject] = "No",
420-
style: str = None,
420+
style: Optional[str] = None,
421421
):
422422
"""
423423
An object that defines a dialog that provides a confirmation step to any
@@ -439,25 +439,25 @@ def __init__(
439439
self.style = self._style
440440

441441
@JsonValidator(f"title attribute cannot exceed {title_max_length} characters")
442-
def title_length(self):
442+
def title_length(self) -> bool:
443443
return self._title is None or len(self._title.text) <= self.title_max_length
444444

445445
@JsonValidator(f"text attribute cannot exceed {text_max_length} characters")
446-
def text_length(self):
446+
def text_length(self) -> bool:
447447
return self._text is None or len(self._text.text) <= self.text_max_length
448448

449449
@JsonValidator(f"confirm attribute cannot exceed {confirm_max_length} characters")
450-
def confirm_length(self):
450+
def confirm_length(self) -> bool:
451451
return (
452452
self._confirm is None or len(self._confirm.text) <= self.confirm_max_length
453453
)
454454

455455
@JsonValidator(f"deny attribute cannot exceed {deny_max_length} characters")
456-
def deny_length(self):
456+
def deny_length(self) -> bool:
457457
return self._deny is None or len(self._deny.text) <= self.deny_max_length
458458

459459
@JsonValidator('style for confirm must be either "primary" or "danger"')
460-
def _validate_confirm_style(self):
460+
def _validate_confirm_style(self) -> bool:
461461
return self._style is None or self._style in ["primary", "danger"]
462462

463463
def to_dict(self, option_type: str = "block") -> dict: # skipcq: PYL-W0221

0 commit comments

Comments
 (0)