From 5933cb0497f05a26312bbbc48f15a35344b7b885 Mon Sep 17 00:00:00 2001 From: William Bergamin Date: Fri, 20 Feb 2026 12:35:25 -0500 Subject: [PATCH 1/3] chore: add AGENTS.md and consolidate development scripts --- .claude/.gitignore | 4 + .claude/CLAUDE.md | 7 + .claude/settings.json | 37 +++ .github/maintainers_guide.md | 4 +- AGENTS.md | 309 ++++++++++++++++++++ scripts/install.sh | 14 + scripts/run_integration_tests.sh | 5 +- scripts/run_mypy.sh | 7 +- scripts/{run_unit_tests.sh => run_tests.sh} | 10 +- scripts/run_validation.sh | 13 +- 10 files changed, 390 insertions(+), 20 deletions(-) create mode 100644 .claude/.gitignore create mode 100644 .claude/CLAUDE.md create mode 100644 .claude/settings.json create mode 100644 AGENTS.md create mode 100755 scripts/install.sh rename scripts/{run_unit_tests.sh => run_tests.sh} (57%) diff --git a/.claude/.gitignore b/.claude/.gitignore new file mode 100644 index 000000000..3a2f7f6a1 --- /dev/null +++ b/.claude/.gitignore @@ -0,0 +1,4 @@ +CLAUDE.local.md +settings.local.json +worktrees/ +plans/ diff --git a/.claude/CLAUDE.md b/.claude/CLAUDE.md new file mode 100644 index 000000000..ebacdb784 --- /dev/null +++ b/.claude/CLAUDE.md @@ -0,0 +1,7 @@ +# CLAUDE.md + +Persistent context, coding standards, and instructions for AI coding agents have been centralized to `AGENTS.md`. + +**CRITICAL INSTRUCTION:** Before answering any questions, writing any code, or executing any commands in this repository, you MUST read the `AGENTS.md` file located in the root directory. + +Proceed by silently reading `./AGENTS.md`, do not ask for permission to read it; just do it. diff --git a/.claude/settings.json b/.claude/settings.json new file mode 100644 index 000000000..d8cba0309 --- /dev/null +++ b/.claude/settings.json @@ -0,0 +1,37 @@ +{ + "permissions": { + "allow": [ + "Bash(./scripts/build_pypi_package.sh:*)", + "Bash(./scripts/format.sh:*)", + "Bash(./scripts/generate_api_docs.sh:*)", + "Bash(./scripts/install.sh:*)", + "Bash(./scripts/install_all_and_run_tests.sh:*)", + "Bash(./scripts/lint.sh:*)", + "Bash(./scripts/run_mypy.sh:*)", + "Bash(./scripts/run_tests.sh:*)", + "Bash(./scripts/run_validation.sh:*)", + "Bash(./scripts/uninstall_all.sh:*)", + "Bash(python scripts/codegen.py:*)", + "Bash(echo $VIRTUAL_ENV)", + "Bash(gh issue view:*)", + "Bash(gh label list:*)", + "Bash(gh pr checks:*)", + "Bash(gh pr diff:*)", + "Bash(gh pr list:*)", + "Bash(gh pr status:*)", + "Bash(gh pr update-branch:*)", + "Bash(gh pr view:*)", + "Bash(gh search code:*)", + "Bash(git diff:*)", + "Bash(git grep:*)", + "Bash(git log:*)", + "Bash(git show:*)", + "Bash(git status:*)", + "Bash(grep:*)", + "Bash(ls:*)", + "Bash(tree:*)", + "WebFetch(domain:github.com)", + "WebFetch(domain:docs.slack.dev)" + ] + } +} diff --git a/.github/maintainers_guide.md b/.github/maintainers_guide.md index ccda1607f..27927530d 100644 --- a/.github/maintainers_guide.md +++ b/.github/maintainers_guide.md @@ -88,13 +88,13 @@ Run all the unit tests, code linter, and code analyzer: Run all the unit tests (no linter nor code analyzer): ```sh -./scripts/run_unit_tests.sh +./scripts/run_tests.sh ``` Run a specific unit test: ```sh -./scripts/run_unit_tests.sh tests/web/test_web_client.py +./scripts/run_tests.sh tests/web/test_web_client.py ``` You can rely on GitHub Actions builds for running the tests on a variety of Python runtimes. diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 000000000..699be142d --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,309 @@ +# AGENTS.md — Python Slack SDK + +## Project Overview + +The Python Slack SDK (`slack_sdk`) is a modular Python library for interacting with the Slack platform APIs. It is published on PyPI as `slack-sdk`. The SDK provides independent packages for each Slack API surface: Web API, Webhooks, Socket Mode, OAuth, Audit Logs, SCIM, RTM, Block Kit models, and request signature verification. + +- **Repository**: +- **Documentation**: +- **PyPI**: +- **Current version**: defined in `slack_sdk/version.py` + +## Critical Rules + +These are the most important constraints in this project. Violating any of them will break CI or corrupt auto-generated code: + +1. **Never edit auto-generated files.** The following files are produced by `scripts/codegen.py` and must not be modified directly: + - `slack_sdk/web/async_client.py` + - `slack_sdk/web/legacy_client.py` + - `slack_sdk/web/async_chat_stream.py` + + Edit the source files (`client.py` or `chat_stream.py`) instead, then run codegen (see [Code Generation](#code-generation-critical-pattern)). + +2. **Zero runtime dependencies.** The core sync Web API client must have no required runtime dependencies. Do not add entries to `install_requires` / `dependencies` in `pyproject.toml`. + +3. **Do not modify the legacy `slack/` package.** It is in maintenance mode and only re-exports from `slack_sdk` with deprecation warnings. All new development goes in `slack_sdk/`. + +4. **Always run codegen + format after editing `client.py` or `chat_stream.py`:** + + ```sh + python scripts/codegen.py --path . + ./scripts/format.sh + ``` + +5. **Use project scripts, not raw tool commands.** Run `./scripts/run_tests.sh`, not `pytest` directly. The scripts handle codegen and formatting. + +## Architecture + +### Package Structure + +```text +slack_sdk/ # Main package (active development) +├── web/ # Web API client (sync, async, legacy) +│ ├── client.py # *** CANONICAL SOURCE — edit this file *** +│ ├── async_client.py # AUTO-GENERATED from client.py (do not edit) +│ ├── legacy_client.py # AUTO-GENERATED from client.py (do not edit) +│ ├── base_client.py # Sync HTTP transport (urllib) +│ ├── async_base_client.py # Async HTTP transport (aiohttp) +│ ├── legacy_base_client.py +│ ├── slack_response.py # Response wrapper (sync) +│ ├── async_slack_response.py +│ ├── chat_stream.py # Streaming chat (edit this file) +│ ├── async_chat_stream.py # AUTO-GENERATED from chat_stream.py (do not edit) +│ └── internal_utils.py # Shared helpers +├── webhook/ # Incoming Webhooks & response_url +├── socket_mode/ # Socket Mode (multiple backend implementations) +│ ├── builtin/ # Built-in WebSocket (no extra deps) +│ ├── aiohttp/ # aiohttp backend +│ ├── websocket_client/ # websocket-client backend +│ └── websockets/ # websockets backend +├── oauth/ # OAuth V2 + OpenID Connect flows +│ ├── installation_store/ # Token storage (file, SQLAlchemy, S3, etc.) +│ ├── state_store/ # OAuth state management +│ └── token_rotation/ # Token refresh logic +├── models/ # Block Kit UI builders +│ ├── blocks/ # Block elements +│ ├── views/ # Modal views +│ ├── attachments/ # Legacy attachments +│ └── metadata/ # Event/entity metadata +├── audit_logs/v1/ # Audit Logs API client +├── scim/v1/ # SCIM API client +├── signature/ # Request signature verification +├── http_retry/ # HTTP retry handlers (builtin sync + async) +├── rtm_v2/ # Real-time messaging (legacy) +├── errors/ # Exception types +└── version.py # Single source of truth for version + +slack/ # Legacy package (maintenance mode, do not modify) +``` + +### Code Generation (Critical Pattern) + +The SDK uses code generation to maintain sync/async/legacy variants from a single source of truth. This is the most important pattern to understand: + +1. **`slack_sdk/web/client.py`** is the canonical source for all Web API methods +2. **`scripts/codegen.py`** transforms `client.py` into: + - `async_client.py` — adds `async def`, `await`, replaces classes with async variants + - `legacy_client.py` — adds `Union[Future, SlackResponse]` return types +3. Similarly, `chat_stream.py` generates `async_chat_stream.py` + +**Never edit auto-generated files directly.** They contain a header: + +```text +# DO NOT EDIT THIS FILE +# 1) Modify slack_sdk/web/client.py +# 2) Run `python scripts/codegen.py` +# 3) Run `black slack_sdk/` +``` + +After editing `client.py` or `chat_stream.py`, always run: + +```sh +python scripts/codegen.py --path . +./scripts/format.sh +``` + +### Web API Method Pattern + +Every Web API method in `client.py` follows this pattern: + +```python +def method_name( + self, + *, # keyword-only arguments + required_param: str, + optional_param: Optional[str] = None, + **kwargs, +) -> SlackResponse: + """Description of the API method + https://docs.slack.dev/reference/methods/method.name + """ + kwargs.update({"required_param": required_param}) + if optional_param is not None: + kwargs.update({"optional_param": optional_param}) + return self.api_call("method.name", params=kwargs) +``` + +Key conventions: + +- All parameters are keyword-only (after `*`) +- Required params have no default; optional params default to `None` +- `**kwargs` captures additional/undocumented parameters +- Parameters are collected into `kwargs` dict and passed to `self.api_call()` +- The `api_call` method name uses Slack's dot-notation (e.g., `"chat.postMessage"`) +- Docstrings include a link to the Slack API reference + +### Error Types + +Defined in `slack_sdk/errors/__init__.py`: + +| Exception | When raised | +| --- | --- | +| `SlackClientError` | Base class for all client errors | +| `SlackApiError` | Invalid API response (carries the `response` object) | +| `SlackRequestError` | Problem submitting the request | +| `BotUserAccessError` | `xoxb` token used for a `xoxp`-only method | +| `SlackTokenRotationError` | `oauth.v2.access` token rotation failure | +| `SlackClientNotConnectedError` | WebSocket operation while disconnected | +| `SlackObjectFormationError` | Malformed Block Kit or other SDK objects | +| `SlackClientConfigurationError` | Invalid client-side configuration | + +### HTTP Retry Handlers + +The `slack_sdk/http_retry/` module provides built-in retry strategies: + +- **`ConnectionErrorRetryHandler`** / **`AsyncConnectionErrorRetryHandler`** — retries on connection errors +- **`RateLimitErrorRetryHandler`** / **`AsyncRateLimitErrorRetryHandler`** — retries on HTTP 429 (respects `Retry-After`) +- **`ServerErrorRetryHandler`** / **`AsyncServerErrorRetryHandler`** — retries on HTTP 500/503 + +Retry interval is configurable via `BackoffRetryIntervalCalculator` (exponential backoff) or `FixedValueRetryIntervalCalculator`. + +### Test Patterns + +Tests use `unittest.TestCase` with a mock web API server: + +```python +import unittest +from slack_sdk import WebClient +from tests.slack_sdk.web.mock_web_api_handler import MockHandler +from tests.mock_web_api_server import setup_mock_web_api_server, cleanup_mock_web_api_server + +class TestFeature(unittest.TestCase): + def setUp(self): + setup_mock_web_api_server(self, MockHandler) + self.client = WebClient( + token="xoxb-api_test", + base_url="http://localhost:8888", + ) + + def tearDown(self): + cleanup_mock_web_api_server(self) + + def test_something(self): + resp = self.client.api_test() + self.assertTrue(resp["ok"]) +``` + +Each sub-package has its own `MockHandler` (e.g., `tests/slack_sdk/webhook/mock_web_api_handler.py`, `tests/slack_sdk/scim/mock_web_api_handler.py`). Use the handler from the matching sub-package. + +Test directories: + +- `tests/` — Unit tests (mirroring `slack_sdk/` structure) + - `tests/slack_sdk/` — Sync tests + - `tests/slack_sdk_async/` — Async variants + - `tests/slack_sdk_fixture/` — Pytest fixtures and test data + - `tests/data/` — JSON fixture files + - `tests/mock_web_api_server/` — Mock Slack API server +- `integration_tests/` — Tests against real Slack APIs (require env tokens) + +## Development Commands + +### Setup + +A python virtual environment (`venv`) MUST be activated before running any commands. You can verify that the virtual environment is active by checking if the `$VIRTUAL_ENV` environment variable is set with `echo $VIRTUAL_ENV`. +If tools like `black`, `flake8`, `mypy`, or `pytest` are not found, ask the user to activate the venv. + +Always use the project scripts instead of calling tools like `pytest` directly. + +### Install Dependencies + +```sh +./scripts/install.sh +``` + +Installs all project dependencies (testing, optional, and tools) via pip. This script is called automatically by `run_validation.sh`, `run_integration_tests.sh`, and `run_mypy.sh`, so you typically don't need to run it separately. + +### Full Validation + +```sh +./scripts/run_validation.sh +``` + +This is the canonical check — CI runs this and the PR template asks contributors to run it. It installs requirements, runs codegen, formats, lints, runs tests with coverage, and runs mypy type checking on Python 3.14. + +### Individual Commands + +| Task | Command | +| --------------------------- | -------------------------------------------------------------------- | +| Install dependencies | `./scripts/install.sh` | +| Uninstall all packages | `./scripts/uninstall_all.sh` | +| Format code | `./scripts/format.sh` | +| Lint (check formatting) | `./scripts/lint.sh` | +| Run all unit tests | `./scripts/run_tests.sh` | +| Run a specific test | `./scripts/run_tests.sh tests/slack_sdk/web/test_web_client.py` | +| Run type checking | `./scripts/run_mypy.sh` | +| Generate async/legacy code | `python scripts/codegen.py --path .` | +| Build PyPI package | `./scripts/build_pypi_package.sh` | +| Generate API docs | `./scripts/generate_api_docs.sh` | + +## Code Style & Tooling + +- **Formatter**: `black` (line length: 125, version pinned in `requirements/tools.txt`) +- **Linter**: `flake8` (line length: 125, configured in `.flake8`) +- **Type checker**: `mypy` (configured in `pyproject.toml`, excludes `scim/` and `rtm/`) +- **Test runner**: `pytest` with `pytest-asyncio` (asyncio_mode = "auto") +- **Coverage**: `pytest-cov` reporting to Codecov +- **Build system**: `setuptools` via `pyproject.toml` + +## CI Pipeline (GitHub Actions) + +Defined in `.github/workflows/ci-build.yml`, runs on push to `main`, all PRs, and daily schedule. Check the workflow file for the current Python version matrix. + +## Key Files + +| File | Purpose | +| ----------------------------------- | -------------------------------------------------------- | +| `slack_sdk/version.py` | Single source of truth for package version | +| `slack_sdk/web/client.py` | Canonical Web API client (~200+ methods) | +| `slack_sdk/web/chat_stream.py` | Canonical streaming chat client | +| `scripts/codegen.py` | Code generator for async/legacy client variants | +| `pyproject.toml` | Project config (build, black, pytest, mypy settings) | +| `.flake8` | Flake8 linting config | +| `requirements/testing.txt` | Test dependencies | +| `requirements/optional.txt` | Optional runtime dependencies (aiohttp, SQLAlchemy, etc) | +| `requirements/tools.txt` | Dev tools (black, flake8, mypy) | +| `.github/workflows/ci-build.yml` | CI pipeline definition | +| `.github/maintainers_guide.md` | Maintainer workflows and release process | + +## Common Contribution Workflows + +### Adding a New Web API Method + +1. Add the method to `slack_sdk/web/client.py` following the existing pattern +2. Run code generation: `python scripts/codegen.py --path .` +3. Run formatter: `./scripts/format.sh` +4. Add tests in `tests/slack_sdk/web/` +5. Validate: `./scripts/run_validation.sh` + +### Adding a New Feature to a Non-Web Module + +1. Implement the sync version in the appropriate `slack_sdk/` subpackage +2. If the module has async variants, implement those as well (not auto-generated for non-web modules) +3. Add tests mirroring the module structure +4. Validate: `./scripts/run_validation.sh` + +### Fixing a Bug + +1. Write a test that reproduces the bug +2. Fix the code (if in `client.py`, run codegen afterward) +3. Validate: `./scripts/run_validation.sh` + +## Versioning & Releases + +- Use the new version mentioned by the maintainer; if they do not provide it, prompt them for it. +- Ensure the new version follows [Semantic Versioning](http://semver.org/) via [PEP 440](https://peps.python.org/pep-0440/) +- Version lives in `slack_sdk/version.py` and is dynamically read by `pyproject.toml` +- Releases are triggered by publishing a GitHub Release, which triggers the PyPI deployment workflow; the maintainer will take care of this +- Commit message format for releases: `chore(release): version X.Y.Z` + +## Dependencies + +The SDK has **zero required runtime dependencies** for the core sync Web API client and it is imperative it remains that way. Optional dependencies enable additional functionality: + +- `aiohttp` — async HTTP client +- `websockets` / `websocket-client` — Socket Mode backends +- `SQLAlchemy` — OAuth token storage +- `boto3` — S3/DynamoDB token storage +- `aiodns` — faster DNS resolution for async + +Version constraints for optional and dev dependencies are pinned in the `requirements/` directory. diff --git a/scripts/install.sh b/scripts/install.sh new file mode 100755 index 000000000..32a3204e8 --- /dev/null +++ b/scripts/install.sh @@ -0,0 +1,14 @@ +#!/bin/bash +# ./scripts/install.sh +# Installs all project dependencies (testing, optional, and tools) + +set -e + +script_dir=$(dirname $0) +cd ${script_dir}/.. + +pip install -U pip + +pip install -U -r requirements/testing.txt \ + -U -r requirements/optional.txt \ + -U -r requirements/tools.txt diff --git a/scripts/run_integration_tests.sh b/scripts/run_integration_tests.sh index 1a6f254cb..7fa598a9b 100755 --- a/scripts/run_integration_tests.sh +++ b/scripts/run_integration_tests.sh @@ -8,10 +8,7 @@ set -e script_dir=`dirname $0` cd ${script_dir}/.. -pip install -U pip -pip install -U -r requirements/testing.txt \ - -U -r requirements/optional.txt \ - -U -r requirements/tools.txt +./scripts/install.sh echo "Generating code ..." && python scripts/codegen.py --path . echo "Running black (code formatter) ..." && ./scripts/format.sh --no-install diff --git a/scripts/run_mypy.sh b/scripts/run_mypy.sh index cc1146f15..e6452d40e 100755 --- a/scripts/run_mypy.sh +++ b/scripts/run_mypy.sh @@ -6,9 +6,8 @@ set -e script_dir=$(dirname $0) cd ${script_dir}/.. -pip install -U pip setuptools wheel -pip install -U -r requirements/testing.txt \ - -U -r requirements/optional.txt \ - -U -r requirements/tools.txt +if [[ "$1" != "--no-install" ]]; then + ./scripts/install.sh +fi mypy --config-file pyproject.toml diff --git a/scripts/run_unit_tests.sh b/scripts/run_tests.sh similarity index 57% rename from scripts/run_unit_tests.sh rename to scripts/run_tests.sh index c8ab0af78..898be91a7 100755 --- a/scripts/run_unit_tests.sh +++ b/scripts/run_tests.sh @@ -1,21 +1,17 @@ #!/bin/bash # Run all the tests or a single test -# all: ./scripts/run_unit_tests.sh -# single: ./scripts/run_unit_tests.sh tests/slack_sdk_async/web/test_web_client_coverage.py +# all: ./scripts/run_tests.sh +# single: ./scripts/run_tests.sh tests/slack_sdk_async/web/test_web_client_coverage.py set -e script_dir=`dirname $0` cd ${script_dir}/.. -pip install -U pip -pip install -U -r requirements/testing.txt \ - -U -r requirements/optional.txt \ - -U -r requirements/tools.txt +test_target="${1:-tests/}" echo "Generating code ..." && python scripts/codegen.py --path . echo "Running black (code formatter) ..." && ./scripts/format.sh --no-install echo "Running tests ..." -test_target="${1:-tests/}" PYTHONPATH=$PWD:$PYTHONPATH pytest $test_target diff --git a/scripts/run_validation.sh b/scripts/run_validation.sh index 366f0d321..db6eef6f9 100755 --- a/scripts/run_validation.sh +++ b/scripts/run_validation.sh @@ -7,9 +7,11 @@ set -e script_dir=`dirname $0` cd ${script_dir}/.. -pip install -U -r requirements/testing.txt \ - -U -r requirements/optional.txt \ - -U -r requirements/tools.txt +# keep in sync with LATEST_SUPPORTED_PY in .github/workflows/ci-build.yml +LATEST_SUPPORTED_PY="3.14" +current_py=$(python --version | sed -E 's/Python ([0-9]+\.[0-9]+).*/\1/') + +./scripts/install.sh echo "Generating code ..." && python scripts/codegen.py --path . echo "Running black (code formatter) ..." && ./scripts/format.sh --no-install @@ -19,3 +21,8 @@ echo "Running linting checks ..." && ./scripts/lint.sh --no-install echo "Running tests with coverage reporting ..." test_target="${1:-tests/}" PYTHONPATH=$PWD:$PYTHONPATH pytest --cov-report=xml --cov=slack_sdk/ $test_target + +# Run mypy type checking only on the latest supported Python version +if [[ "$current_py" == "$LATEST_SUPPORTED_PY" ]]; then + echo "Running mypy type checking ..." && ./scripts/run_mypy.sh --no-install +fi From bbb8ebf617901a79bae14762269731e56dca2b74 Mon Sep 17 00:00:00 2001 From: William Bergamin Date: Fri, 20 Feb 2026 13:26:18 -0500 Subject: [PATCH 2/3] improve based on feedback --- .claude/settings.json | 1 - AGENTS.md | 205 +++++++++++++++++++++--------------------- 2 files changed, 102 insertions(+), 104 deletions(-) diff --git a/.claude/settings.json b/.claude/settings.json index d8cba0309..4006f7bd9 100644 --- a/.claude/settings.json +++ b/.claude/settings.json @@ -5,7 +5,6 @@ "Bash(./scripts/format.sh:*)", "Bash(./scripts/generate_api_docs.sh:*)", "Bash(./scripts/install.sh:*)", - "Bash(./scripts/install_all_and_run_tests.sh:*)", "Bash(./scripts/lint.sh:*)", "Bash(./scripts/run_mypy.sh:*)", "Bash(./scripts/run_tests.sh:*)", diff --git a/AGENTS.md b/AGENTS.md index 699be142d..3f59d5c0a 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -37,58 +37,29 @@ These are the most important constraints in this project. Violating any of them ### Package Structure -```text -slack_sdk/ # Main package (active development) -├── web/ # Web API client (sync, async, legacy) -│ ├── client.py # *** CANONICAL SOURCE — edit this file *** -│ ├── async_client.py # AUTO-GENERATED from client.py (do not edit) -│ ├── legacy_client.py # AUTO-GENERATED from client.py (do not edit) -│ ├── base_client.py # Sync HTTP transport (urllib) -│ ├── async_base_client.py # Async HTTP transport (aiohttp) -│ ├── legacy_base_client.py -│ ├── slack_response.py # Response wrapper (sync) -│ ├── async_slack_response.py -│ ├── chat_stream.py # Streaming chat (edit this file) -│ ├── async_chat_stream.py # AUTO-GENERATED from chat_stream.py (do not edit) -│ └── internal_utils.py # Shared helpers -├── webhook/ # Incoming Webhooks & response_url -├── socket_mode/ # Socket Mode (multiple backend implementations) -│ ├── builtin/ # Built-in WebSocket (no extra deps) -│ ├── aiohttp/ # aiohttp backend -│ ├── websocket_client/ # websocket-client backend -│ └── websockets/ # websockets backend -├── oauth/ # OAuth V2 + OpenID Connect flows -│ ├── installation_store/ # Token storage (file, SQLAlchemy, S3, etc.) -│ ├── state_store/ # OAuth state management -│ └── token_rotation/ # Token refresh logic -├── models/ # Block Kit UI builders -│ ├── blocks/ # Block elements -│ ├── views/ # Modal views -│ ├── attachments/ # Legacy attachments -│ └── metadata/ # Event/entity metadata -├── audit_logs/v1/ # Audit Logs API client -├── scim/v1/ # SCIM API client -├── signature/ # Request signature verification -├── http_retry/ # HTTP retry handlers (builtin sync + async) -├── rtm_v2/ # Real-time messaging (legacy) -├── errors/ # Exception types -└── version.py # Single source of truth for version - -slack/ # Legacy package (maintenance mode, do not modify) -``` +The SDK is organized into independent sub-packages: -### Code Generation (Critical Pattern) +- **`slack_sdk/web/`** — Web API client (sync, async, legacy). Contains auto-generated files (see [Code Generation](#code-generation-critical-pattern)) +- **`slack_sdk/webhook/`** — Incoming Webhooks +- **`slack_sdk/socket_mode/`** — Socket Mode with pluggable backends +- **`slack_sdk/oauth/`** — OAuth flows and token storage +- **`slack_sdk/models/`** — Block Kit UI builders +- **`slack_sdk/audit_logs/`**, **`slack_sdk/scim/`** — Enterprise APIs +- **`slack_sdk/signature/`** — Request verification +- **`slack_sdk/http_retry/`** — Retry handlers +- **`slack/`** — Legacy package (maintenance mode, do not modify) -The SDK uses code generation to maintain sync/async/legacy variants from a single source of truth. This is the most important pattern to understand: +See the repository structure for the complete package layout. -1. **`slack_sdk/web/client.py`** is the canonical source for all Web API methods -2. **`scripts/codegen.py`** transforms `client.py` into: - - `async_client.py` — adds `async def`, `await`, replaces classes with async variants - - `legacy_client.py` — adds `Union[Future, SlackResponse]` return types -3. Similarly, `chat_stream.py` generates `async_chat_stream.py` +### Code Generation (Critical Pattern) -**Never edit auto-generated files directly.** They contain a header: +**NEVER edit these auto-generated files:** +- `slack_sdk/web/async_client.py` +- `slack_sdk/web/legacy_client.py` +- `slack_sdk/web/async_chat_stream.py` + +Each contains a header warning: ```text # DO NOT EDIT THIS FILE # 1) Modify slack_sdk/web/client.py @@ -96,12 +67,14 @@ The SDK uses code generation to maintain sync/async/legacy variants from a singl # 3) Run `black slack_sdk/` ``` -After editing `client.py` or `chat_stream.py`, always run: +**How it works:** -```sh -python scripts/codegen.py --path . -./scripts/format.sh -``` +1. Edit `slack_sdk/web/client.py` (canonical source for Web API methods) +2. Edit `slack_sdk/web/chat_stream.py` (canonical source for streaming chat) +3. Run `python scripts/codegen.py --path .` to generate async/legacy variants +4. Run `./scripts/format.sh` to format the generated code + +The codegen script (`scripts/codegen.py`) automatically transforms sync code into async variants by adding `async def`, `await`, and replacing classes with async equivalents. ### Web API Method Pattern @@ -135,28 +108,27 @@ Key conventions: ### Error Types -Defined in `slack_sdk/errors/__init__.py`: +All SDK exceptions are defined in `slack_sdk/errors/__init__.py` and inherit from `SlackClientError`. -| Exception | When raised | -| --- | --- | -| `SlackClientError` | Base class for all client errors | -| `SlackApiError` | Invalid API response (carries the `response` object) | -| `SlackRequestError` | Problem submitting the request | -| `BotUserAccessError` | `xoxb` token used for a `xoxp`-only method | -| `SlackTokenRotationError` | `oauth.v2.access` token rotation failure | -| `SlackClientNotConnectedError` | WebSocket operation while disconnected | -| `SlackObjectFormationError` | Malformed Block Kit or other SDK objects | -| `SlackClientConfigurationError` | Invalid client-side configuration | +Key exceptions to be aware of: + +- **`SlackApiError`** — Raised when the API returns an error response (carries the `response` object) +- **`SlackRequestError`** — Raised when the HTTP request itself fails +- **`BotUserAccessError`** — Raised when using a bot token (`xoxb-*`) for a user-only method + +See `slack_sdk/errors/__init__.py` for the complete list of exception types and their usage. ### HTTP Retry Handlers -The `slack_sdk/http_retry/` module provides built-in retry strategies: +The `slack_sdk/http_retry/` module provides built-in retry strategies for connection errors, rate limiting (HTTP 429), and server errors (HTTP 500/503). -- **`ConnectionErrorRetryHandler`** / **`AsyncConnectionErrorRetryHandler`** — retries on connection errors -- **`RateLimitErrorRetryHandler`** / **`AsyncRateLimitErrorRetryHandler`** — retries on HTTP 429 (respects `Retry-After`) -- **`ServerErrorRetryHandler`** / **`AsyncServerErrorRetryHandler`** — retries on HTTP 500/503 +Key handlers: -Retry interval is configurable via `BackoffRetryIntervalCalculator` (exponential backoff) or `FixedValueRetryIntervalCalculator`. +- **`ConnectionErrorRetryHandler`** / **`AsyncConnectionErrorRetryHandler`** +- **`RateLimitErrorRetryHandler`** / **`AsyncRateLimitErrorRetryHandler`** (respects `Retry-After` header) +- **`ServerErrorRetryHandler`** / **`AsyncServerErrorRetryHandler`** + +Retry intervals can be configured with `BackoffRetryIntervalCalculator` (exponential backoff) or `FixedValueRetryIntervalCalculator`. See `slack_sdk/http_retry/` for implementation details. ### Test Patterns @@ -200,8 +172,9 @@ Test directories: ### Setup -A python virtual environment (`venv`) MUST be activated before running any commands. You can verify that the virtual environment is active by checking if the `$VIRTUAL_ENV` environment variable is set with `echo $VIRTUAL_ENV`. -If tools like `black`, `flake8`, `mypy`, or `pytest` are not found, ask the user to activate the venv. +**Prerequisites:** A Python virtual environment must be activated. See `.github/maintainers_guide.md` for detailed setup instructions using `pyenv` and `venv`. + +**Quick check:** Verify venv is active with `echo $VIRTUAL_ENV` (should output a path). Always use the project scripts instead of calling tools like `pytest` directly. @@ -219,51 +192,77 @@ Installs all project dependencies (testing, optional, and tools) via pip. This s ./scripts/run_validation.sh ``` -This is the canonical check — CI runs this and the PR template asks contributors to run it. It installs requirements, runs codegen, formats, lints, runs tests with coverage, and runs mypy type checking on Python 3.14. +This is the canonical check — CI runs this and the PR template asks contributors to run it. It installs requirements, runs codegen, formats, lints, runs tests with coverage, and runs mypy type checking on the latest supported Python version (check the `LATEST_SUPPORTED_PY` environment variable in `.github/workflows/ci-build.yml`). ### Individual Commands -| Task | Command | -| --------------------------- | -------------------------------------------------------------------- | -| Install dependencies | `./scripts/install.sh` | -| Uninstall all packages | `./scripts/uninstall_all.sh` | -| Format code | `./scripts/format.sh` | -| Lint (check formatting) | `./scripts/lint.sh` | -| Run all unit tests | `./scripts/run_tests.sh` | -| Run a specific test | `./scripts/run_tests.sh tests/slack_sdk/web/test_web_client.py` | -| Run type checking | `./scripts/run_mypy.sh` | -| Generate async/legacy code | `python scripts/codegen.py --path .` | -| Build PyPI package | `./scripts/build_pypi_package.sh` | -| Generate API docs | `./scripts/generate_api_docs.sh` | +Available scripts in the `scripts/` directory: + +| Task | Command | +| --- | --- | +| Install dependencies | `./scripts/install.sh` | +| Uninstall all packages | `./scripts/uninstall_all.sh` | +| Format code | `./scripts/format.sh` | +| Lint (check formatting) | `./scripts/lint.sh` | +| Run all unit tests | `./scripts/run_tests.sh` | +| Run a specific test | `./scripts/run_tests.sh tests/slack_sdk/web/test_web_client.py` | +| Run type checking | `./scripts/run_mypy.sh` | +| Generate async/legacy code | `python scripts/codegen.py --path .` | +| Build PyPI package | `./scripts/build_pypi_package.sh` | +| Generate API docs | `./scripts/generate_api_docs.sh` | ## Code Style & Tooling -- **Formatter**: `black` (line length: 125, version pinned in `requirements/tools.txt`) -- **Linter**: `flake8` (line length: 125, configured in `.flake8`) -- **Type checker**: `mypy` (configured in `pyproject.toml`, excludes `scim/` and `rtm/`) -- **Test runner**: `pytest` with `pytest-asyncio` (asyncio_mode = "auto") +All tooling configuration is defined in the following files: + +- **Formatter**: `black` — see `[tool.black]` in `pyproject.toml` +- **Linter**: `flake8` — see `.flake8` +- **Type checker**: `mypy` — see `[tool.mypy]` in `pyproject.toml` +- **Test runner**: `pytest` — see `[tool.pytest.ini_options]` in `pyproject.toml` - **Coverage**: `pytest-cov` reporting to Codecov -- **Build system**: `setuptools` via `pyproject.toml` +- **Build system**: see `[build-system]` and `[project]` in `pyproject.toml` + +**Dependencies:** + +- Testing: `requirements/testing.txt` +- Optional runtime: `requirements/optional.txt` +- Dev tools (black, flake8, mypy): `requirements/tools.txt` ## CI Pipeline (GitHub Actions) Defined in `.github/workflows/ci-build.yml`, runs on push to `main`, all PRs, and daily schedule. Check the workflow file for the current Python version matrix. -## Key Files - -| File | Purpose | -| ----------------------------------- | -------------------------------------------------------- | -| `slack_sdk/version.py` | Single source of truth for package version | -| `slack_sdk/web/client.py` | Canonical Web API client (~200+ methods) | -| `slack_sdk/web/chat_stream.py` | Canonical streaming chat client | -| `scripts/codegen.py` | Code generator for async/legacy client variants | -| `pyproject.toml` | Project config (build, black, pytest, mypy settings) | -| `.flake8` | Flake8 linting config | -| `requirements/testing.txt` | Test dependencies | -| `requirements/optional.txt` | Optional runtime dependencies (aiohttp, SQLAlchemy, etc) | -| `requirements/tools.txt` | Dev tools (black, flake8, mypy) | -| `.github/workflows/ci-build.yml` | CI pipeline definition | -| `.github/maintainers_guide.md` | Maintainer workflows and release process | +## Key Files & Directories + +**Source Code:** + +- `slack_sdk/` — Main package (active development) +- `slack_sdk/web/client.py` — **CANONICAL SOURCE** for all Web API methods +- `slack_sdk/web/chat_stream.py` — Canonical streaming chat client +- `slack_sdk/version.py` — Single source of truth for version +- `slack/` — Legacy package (maintenance mode, DO NOT MODIFY) + +**Configuration:** + +- `pyproject.toml` — Project metadata, build config, tool settings (black, pytest, mypy) +- `.flake8` — Flake8 linter configuration +- `requirements/*.txt` — Dependency specifications + +**Tooling:** + +- `scripts/codegen.py` — Generates async/legacy client variants +- `scripts/*.sh` — Development and CI helper scripts + +**GitHub & CI/CD:** + +- `.github/` — GitHub-specific configuration and documentation +- `.github/workflows/` — Continuous integration pipeline definitions that run on GitHub Actions +- `.github/maintainers_guide.md` — Maintainer workflows and release process + +**Documentation:** + +- `README.md` — Project overview, installation, and usage examples +- `.github/maintainers_guide.md` — Maintainer workflows and release process ## Common Contribution Workflows From 1fad98e3cd0309da8e9ec1d9eb1157579d085c36 Mon Sep 17 00:00:00 2001 From: William Bergamin Date: Tue, 24 Feb 2026 07:13:28 -0800 Subject: [PATCH 3/3] Update .claude/CLAUDE.md Co-authored-by: Eden Zimbelman --- .claude/CLAUDE.md | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/.claude/CLAUDE.md b/.claude/CLAUDE.md index ebacdb784..dba71e970 100644 --- a/.claude/CLAUDE.md +++ b/.claude/CLAUDE.md @@ -1,7 +1 @@ -# CLAUDE.md - -Persistent context, coding standards, and instructions for AI coding agents have been centralized to `AGENTS.md`. - -**CRITICAL INSTRUCTION:** Before answering any questions, writing any code, or executing any commands in this repository, you MUST read the `AGENTS.md` file located in the root directory. - -Proceed by silently reading `./AGENTS.md`, do not ask for permission to read it; just do it. +@../AGENTS.md