Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 0 additions & 3 deletions .flake8

This file was deleted.

3 changes: 3 additions & 0 deletions .git-blame-ignore-revs
Original file line number Diff line number Diff line change
@@ -1,2 +1,5 @@
# change black settings
0e4cd56b69e8f83166cd262f762802b7f18c3d21

# apply ruff format across the codebase
07e8ac9d98c535ade20040883a04734b4c9d04b8
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@ pip
# JetBrains PyCharm settings
.idea/

# VS Code settings
.vscode/

tmp.txt
.DS_Store
logs/
Expand Down
8 changes: 4 additions & 4 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ Slack Bolt for Python -- a framework for building Slack apps in Python.

## Environment Setup

You can verify the venv is active by checking `echo $VIRTUAL_ENV`. If tools like `black`, `flake8`, `mypy` or `pytest` are not found, ask the user to activate the venv.
You can verify the venv is active by checking `echo $VIRTUAL_ENV`. If tools like `ruff`, `mypy` or `pytest` are not found, ask the user to activate the venv.

A python virtual environment (`venv`) should be activated before running any commands.

Expand Down Expand Up @@ -65,10 +65,10 @@ Always use the project scripts instead of calling `pytest` directly:
### Formatting, Linting, Type Checking

```bash
# Format -- Black, configured in pyproject.toml
# Format -- Ruff formatter (+ lint autofix), configured in pyproject.toml
./scripts/format.sh --no-install

# Lint -- Flake8, configured in .flake8
# Lint -- Ruff linter, configured in pyproject.toml
./scripts/lint.sh --no-install

# Type check -- mypy, configured in pyproject.toml
Expand Down Expand Up @@ -210,7 +210,7 @@ The core package has a **single required runtime dependency**: `slack_sdk` (defi
- `test_async.txt` -- test runner deps (`pytest`, `pytest-asyncio`, includes `async_dev.txt`)
- `test.txt` -- test deps without async (`pytest`, `pytest-cov`)
- `test_adapter.txt` -- adapter-specific test deps (`moto`, `boddle`, `sanic-testing`)
- `dev_tools.txt` -- dev tools (`mypy`, `flake8`, `black`)
- `dev_tools.txt` -- dev tools (`mypy`, `ruff`)

When adding a new dependency: add it to the appropriate `requirements/*.txt` file with version constraints, never to `pyproject.toml` `dependencies` (unless it's a core runtime dep, which is very rare).

Expand Down
2 changes: 1 addition & 1 deletion examples/assistants/async_interaction_app.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# flake8: noqa F811
# ruff: noqa: F811
import asyncio
import logging
import os
Expand Down
6 changes: 5 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,13 @@ readme = { file = ["README.md"], content-type = "text/markdown" }
[tool.distutils.bdist_wheel]
universal = true

[tool.black]
[tool.ruff]
line-length = 125

[tool.ruff.lint]
select = ["E", "W", "F"]
ignore = ["F841", "F821", "E402"]

[tool.pytest.ini_options]
testpaths = ["tests"]
log_file = "logs/pytest.log"
Expand Down
7 changes: 2 additions & 5 deletions requirements/dev_tools.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,5 @@
# mypy
mypy==2.3.0

# flake8
flake8==7.3.0

# black
black==26.3.1
# ruff
ruff==0.16.4
3 changes: 2 additions & 1 deletion scripts/format.sh
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,5 @@ if [[ "$1" != "--no-install" ]]; then
pip install -U -r requirements/dev_tools.txt
fi

black slack_bolt/ tests/
ruff check --fix slack_bolt/ examples/
ruff format slack_bolt/ tests/
2 changes: 1 addition & 1 deletion scripts/lint.sh
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,4 @@ if [[ "$1" != "--no-install" ]]; then
pip install -U -r requirements/dev_tools.txt
fi

flake8 slack_bolt/ && flake8 examples/
ruff check slack_bolt/ examples/
2 changes: 1 addition & 1 deletion slack_bolt/adapter/asgi/base_handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ async def __call__(self, scope: scope_type, receive: Callable, send: Callable) -
if scope["type"] == "http":
response: AsgiHttpResponse = await self._get_http_response(
method=scope["method"], path=scope["path"], request=AsgiHttpRequest(scope, receive) # type: ignore[arg-type]
)
) # fmt: skip
await send(response.get_response_start())
await send(response.get_response_body())
return
Expand Down
3 changes: 1 addition & 2 deletions slack_bolt/adapter/django/handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,8 +66,7 @@ def release_thread_local_connections(logger: Logger, execution_timing: str):
if logger.level <= logging.DEBUG:
current: Thread = current_thread()
logger.debug(
"Released thread-bound old DB connections "
f"(thread name: {current.name}, execution timing: {execution_timing})"
f"Released thread-bound old DB connections (thread name: {current.name}, execution timing: {execution_timing})"
)


Expand Down
2 changes: 1 addition & 1 deletion slack_bolt/app/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# flake8: noqa
# ruff: noqa
"""Application interface in Bolt.

For most use cases, we recommend using `slack_bolt.app.app`.
Expand Down
2 changes: 1 addition & 1 deletion slack_bolt/app/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -589,7 +589,7 @@ def middleware_next():
# run all the middleware attached to this listener first
middleware_resp, next_was_not_called = listener.run_middleware(
req=req, resp=resp # type: ignore[arg-type]
)
) # fmt: skip
if next_was_not_called:
if middleware_resp is not None:
if self._framework_logger.level <= logging.DEBUG:
Expand Down
4 changes: 2 additions & 2 deletions slack_bolt/app/async_app.py
Original file line number Diff line number Diff line change
Expand Up @@ -588,7 +588,7 @@ async def async_middleware_next():
self._framework_logger.debug(f"Applying {middleware.name}")
resp = await middleware.async_process(
req=req, resp=resp, next=async_middleware_next # type: ignore[arg-type]
)
) # fmt: skip
if not middleware_state["next_called"]:
if resp is None:
# next() method was not called without providing the response to return to Slack
Expand Down Expand Up @@ -619,7 +619,7 @@ async def async_middleware_next():
# run all the middleware attached to this listener first
middleware_resp, next_was_not_called = await listener.run_async_middleware(
req=req, resp=resp # type: ignore[arg-type]
)
) # fmt: skip
if next_was_not_called:
if middleware_resp is not None:
if self._framework_logger.level <= logging.DEBUG:
Expand Down
2 changes: 1 addition & 1 deletion slack_bolt/authorization/async_authorize.py
Original file line number Diff line number Diff line change
Expand Up @@ -356,7 +356,7 @@ async def __call__(
# ------------------------------------------------

def _debug_log_for_not_found(self, enterprise_id: Optional[str], team_id: Optional[str]):
self.logger.debug("No installation data found " f"for enterprise_id: {enterprise_id} team_id: {team_id}")
self.logger.debug(f"No installation data found for enterprise_id: {enterprise_id} team_id: {team_id}")

async def _rotate_and_save_tokens_if_necessary(self, installation: Optional[Installation]) -> Optional[Installation]:
if installation is None or (installation.user_refresh_token is None and installation.bot_refresh_token is None):
Expand Down
2 changes: 1 addition & 1 deletion slack_bolt/authorization/authorize.py
Original file line number Diff line number Diff line change
Expand Up @@ -353,7 +353,7 @@ def __call__(
# ------------------------------------------------

def _debug_log_for_not_found(self, enterprise_id: Optional[str], team_id: Optional[str]):
self.logger.debug("No installation data found " f"for enterprise_id: {enterprise_id} team_id: {team_id}")
self.logger.debug(f"No installation data found for enterprise_id: {enterprise_id} team_id: {team_id}")

def _rotate_and_save_tokens_if_necessary(self, installation: Optional[Installation]) -> Optional[Installation]:
if installation is None or (installation.user_refresh_token is None and installation.bot_refresh_token is None):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@


class FileAssistantThreadContextStore(AssistantThreadContextStore):

def __init__(
self,
base_dir: str = str(Path.home()) + "/.bolt-app-assistant-thread-contexts",
Expand Down
58 changes: 28 additions & 30 deletions slack_bolt/logger/messages.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ def error_auth_test_failure(error_response: SlackResponse) -> str:


def error_token_required() -> str:
return "Either an env variable `SLACK_BOT_TOKEN` " "or `token` argument in the constructor is required."
return "Either an env variable `SLACK_BOT_TOKEN` or `token` argument in the constructor is required."


def error_unexpected_listener_middleware(middleware_type) -> str:
Expand Down Expand Up @@ -89,17 +89,16 @@ def warning_client_prioritized_and_token_skipped() -> str:


def warning_token_skipped() -> str:
return (
"As `installation_store` or `authorize` has been used, " "`token` (or SLACK_BOT_TOKEN env variable) will be ignored."
)
return "As `installation_store` or `authorize` has been used, `token` (or SLACK_BOT_TOKEN env variable) will be ignored."


def warning_installation_store_conflicts() -> str:
return "As you gave both `installation_store` and `oauth_settings`/`auth_flow`, the top level one is unused."


def warning_unhandled_by_global_middleware(
name: str, req: Union[BoltRequest, "AsyncBoltRequest"] # type: ignore[name-defined]
name: str,
req: Union[BoltRequest, "AsyncBoltRequest"], # type: ignore[name-defined]
) -> str:
return (
f"A global middleware ({name}) skipped calling either `next()` or `next_()` "
Expand Down Expand Up @@ -194,8 +193,8 @@ def warning_unhandled_request(
return _build_unhandled_request_suggestion(
default_message,
f"""
from slack_bolt.workflows.step{'.async_step' if is_async else ''} import {'Async' if is_async else ''}WorkflowStep
ws = {'Async' if is_async else ''}WorkflowStep(
from slack_bolt.workflows.step{".async_step" if is_async else ""} import {"Async" if is_async else ""}WorkflowStep
ws = {"Async" if is_async else ""}WorkflowStep(
callback_id="{callback_id}",
edit=edit,
save=save,
Expand All @@ -214,8 +213,8 @@ def warning_unhandled_request(
default_message,
f"""
@app.action("{action_id_or_callback_id}")
{'async ' if is_async else ''}def handle_some_action(ack, body, logger):
{'await ' if is_async else ''}ack()
{"async " if is_async else ""}def handle_some_action(ack, body, logger):
{"await " if is_async else ""}ack()
logger.info(body)
""",
)
Expand All @@ -225,13 +224,13 @@ def warning_unhandled_request(
if req.body.get("action_id") is not None:
constraints = '"' + req.body["action_id"] + '"'
elif req.body.get("type") == "dialog_suggestion":
constraints = f"""{{"type": "dialog_suggestion", "callback_id": "{req.body.get('callback_id')}"}}"""
constraints = f"""{{"type": "dialog_suggestion", "callback_id": "{req.body.get("callback_id")}"}}"""
return _build_unhandled_request_suggestion(
default_message,
f"""
@app.options({constraints})
{'async ' if is_async else ''}def handle_some_options(ack):
{'await ' if is_async else ''}ack(options=[ ... ])
{"async " if is_async else ""}def handle_some_options(ack):
{"await " if is_async else ""}ack(options=[ ... ])
""",
)
if is_shortcut(req.body):
Expand All @@ -241,8 +240,8 @@ def warning_unhandled_request(
default_message,
f"""
@app.shortcut("{id}")
{'async ' if is_async else ''}def handle_shortcuts(ack, body, logger):
{'await ' if is_async else ''}ack()
{"async " if is_async else ""}def handle_shortcuts(ack, body, logger):
{"await " if is_async else ""}ack()
logger.info(body)
""",
)
Expand All @@ -251,9 +250,9 @@ def warning_unhandled_request(
return _build_unhandled_request_suggestion(
default_message,
f"""
@app.view("{req.body.get('view', {}).get('callback_id', 'modal-view-id')}")
{'async ' if is_async else ''}def handle_view_submission_events(ack, body, logger):
{'await ' if is_async else ''}ack()
@app.view("{req.body.get("view", {}).get("callback_id", "modal-view-id")}")
{"async " if is_async else ""}def handle_view_submission_events(ack, body, logger):
{"await " if is_async else ""}ack()
logger.info(body)
""",
)
Expand All @@ -262,9 +261,9 @@ def warning_unhandled_request(
return _build_unhandled_request_suggestion(
default_message,
f"""
@app.view_closed("{req.body.get('view', {}).get('callback_id', 'modal-view-id')}")
{'async ' if is_async else ''}def handle_view_closed_events(ack, body, logger):
{'await ' if is_async else ''}ack()
@app.view_closed("{req.body.get("view", {}).get("callback_id", "modal-view-id")}")
{"async " if is_async else ""}def handle_view_closed_events(ack, body, logger):
{"await " if is_async else ""}ack()
logger.info(body)
""",
)
Expand All @@ -279,23 +278,23 @@ def warning_unhandled_request(
default_message,
f"""
@app.function("{callback_id}")
{'async ' if is_async else ''}def handle_some_function(ack, body, complete, fail, logger):
{'await ' if is_async else ''}ack()
{"async " if is_async else ""}def handle_some_function(ack, body, complete, fail, logger):
{"await " if is_async else ""}ack()
logger.info(body)
try:
# TODO: do something here
outputs = {{}}
{'await ' if is_async else ''}complete(outputs=outputs)
{"await " if is_async else ""}complete(outputs=outputs)
except Exception as e:
error = f"Failed to handle a function request (error: {{e}})"
{'await ' if is_async else ''}fail(error=error)
{"await " if is_async else ""}fail(error=error)
""",
)
return _build_unhandled_request_suggestion(
default_message,
f"""
@app.event("{event_type}")
{'async ' if is_async else ''}def handle_{event_type}_events(body, logger):
{"async " if is_async else ""}def handle_{event_type}_events(body, logger):
logger.info(body)
""",
)
Expand All @@ -306,8 +305,8 @@ def warning_unhandled_request(
default_message,
f"""
@app.command("{command}")
{'async ' if is_async else ''}def handle_some_command(ack, body, logger):
{'await ' if is_async else ''}ack()
{"async " if is_async else ""}def handle_some_command(ack, body, logger):
{"await " if is_async else ""}ack()
logger.info(body)
""",
)
Expand All @@ -320,8 +319,7 @@ def warning_did_not_call_ack(listener_name: str) -> str:

def warning_bot_only_conflicts() -> str:
return (
"installation_store_bot_only exists in both App and OAuthFlow.settings. "
"The one passed in App constructor is used."
"installation_store_bot_only exists in both App and OAuthFlow.settings. The one passed in App constructor is used."
)


Expand All @@ -334,7 +332,7 @@ def warning_skip_uncommon_arg_name(arg_name: str) -> str:

def warning_ack_timeout_has_no_effect(identifier: Union[str, Pattern], ack_timeout: int) -> str:
handler_example = f'@app.function("{identifier}")' if isinstance(identifier, str) else f"@app.function({identifier})"
return f"On {handler_example}, as `auto_acknowledge` is `True`, " f"`ack_timeout={ack_timeout}` you gave will be unused"
return f"On {handler_example}, as `auto_acknowledge` is `True`, `ack_timeout={ack_timeout}` you gave will be unused"


# -------------------------------
Expand Down
4 changes: 1 addition & 3 deletions slack_bolt/middleware/assistant/assistant.py
Original file line number Diff line number Diff line change
Expand Up @@ -213,9 +213,7 @@ def _merge_matchers(
primary_matcher: Callable[..., bool],
custom_matchers: Optional[Union[Callable[..., bool], ListenerMatcher]],
):
return [CustomListenerMatcher(app_name=self.app_name, func=primary_matcher)] + (
custom_matchers or []
) # type: ignore[operator]
return [CustomListenerMatcher(app_name=self.app_name, func=primary_matcher)] + (custom_matchers or []) # type: ignore[operator]

@staticmethod
def default_thread_context_changed(save_thread_context: SaveThreadContext, payload: dict):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@


class AsyncAttachingConversationKwargs(AsyncMiddleware):

thread_context_store: Optional[AsyncAssistantThreadContextStore]

def __init__(self, thread_context_store: Optional[AsyncAssistantThreadContextStore] = None):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@


class AttachingConversationKwargs(Middleware):

thread_context_store: Optional[AssistantThreadContextStore]

def __init__(self, thread_context_store: Optional[AssistantThreadContextStore] = None):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,5 +65,5 @@ def _build_error_response() -> BoltResponse:

def _debug_log_error(self, signature, timestamp, body) -> None:
self.logger.info(
"Invalid request signature detected " f"(signature: {signature}, timestamp: {timestamp}, body: {body})"
f"Invalid request signature detected (signature: {signature}, timestamp: {timestamp}, body: {body})"
)
2 changes: 1 addition & 1 deletion slack_bolt/oauth/internals.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ def _build_callback_failure_response(
status: int = 500,
error: Optional[Exception] = None,
) -> BoltResponse:
debug_message = "Handling an OAuth callback failure " f"(reason: {reason}, error: {error}, request: {request.query})"
debug_message = f"Handling an OAuth callback failure (reason: {reason}, error: {error}, request: {request.query})"
self._logger.debug(debug_message)

# Adding a bit more details to the error code to help installers understand what's happening.
Expand Down
Loading