Skip to content

Commit 1d765dc

Browse files
chore: add AGENTS.md and consolidate development scripts (#1836)
Co-authored-by: Eden Zimbelman <eden.zimbelman@salesforce.com>
1 parent f14e689 commit 1d765dc

File tree

10 files changed

+382
-20
lines changed

10 files changed

+382
-20
lines changed

.claude/.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
CLAUDE.local.md
2+
settings.local.json
3+
worktrees/
4+
plans/

.claude/CLAUDE.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
@../AGENTS.md

.claude/settings.json

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
{
2+
"permissions": {
3+
"allow": [
4+
"Bash(./scripts/build_pypi_package.sh:*)",
5+
"Bash(./scripts/format.sh:*)",
6+
"Bash(./scripts/generate_api_docs.sh:*)",
7+
"Bash(./scripts/install.sh:*)",
8+
"Bash(./scripts/lint.sh:*)",
9+
"Bash(./scripts/run_mypy.sh:*)",
10+
"Bash(./scripts/run_tests.sh:*)",
11+
"Bash(./scripts/run_validation.sh:*)",
12+
"Bash(./scripts/uninstall_all.sh:*)",
13+
"Bash(python scripts/codegen.py:*)",
14+
"Bash(echo $VIRTUAL_ENV)",
15+
"Bash(gh issue view:*)",
16+
"Bash(gh label list:*)",
17+
"Bash(gh pr checks:*)",
18+
"Bash(gh pr diff:*)",
19+
"Bash(gh pr list:*)",
20+
"Bash(gh pr status:*)",
21+
"Bash(gh pr update-branch:*)",
22+
"Bash(gh pr view:*)",
23+
"Bash(gh search code:*)",
24+
"Bash(git diff:*)",
25+
"Bash(git grep:*)",
26+
"Bash(git log:*)",
27+
"Bash(git show:*)",
28+
"Bash(git status:*)",
29+
"Bash(grep:*)",
30+
"Bash(ls:*)",
31+
"Bash(tree:*)",
32+
"WebFetch(domain:github.com)",
33+
"WebFetch(domain:docs.slack.dev)"
34+
]
35+
}
36+
}

.github/maintainers_guide.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -88,13 +88,13 @@ Run all the unit tests, code linter, and code analyzer:
8888
Run all the unit tests (no linter nor code analyzer):
8989

9090
```sh
91-
./scripts/run_unit_tests.sh
91+
./scripts/run_tests.sh
9292
```
9393

9494
Run a specific unit test:
9595

9696
```sh
97-
./scripts/run_unit_tests.sh tests/web/test_web_client.py
97+
./scripts/run_tests.sh tests/web/test_web_client.py
9898
```
9999

100100
You can rely on GitHub Actions builds for running the tests on a variety of Python runtimes.

AGENTS.md

Lines changed: 308 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,308 @@
1+
# AGENTS.md — Python Slack SDK
2+
3+
## Project Overview
4+
5+
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.
6+
7+
- **Repository**: <https://github.com/slackapi/python-slack-sdk>
8+
- **Documentation**: <https://docs.slack.dev/tools/python-slack-sdk/>
9+
- **PyPI**: <https://pypi.org/project/slack-sdk/>
10+
- **Current version**: defined in `slack_sdk/version.py`
11+
12+
## Critical Rules
13+
14+
These are the most important constraints in this project. Violating any of them will break CI or corrupt auto-generated code:
15+
16+
1. **Never edit auto-generated files.** The following files are produced by `scripts/codegen.py` and must not be modified directly:
17+
- `slack_sdk/web/async_client.py`
18+
- `slack_sdk/web/legacy_client.py`
19+
- `slack_sdk/web/async_chat_stream.py`
20+
21+
Edit the source files (`client.py` or `chat_stream.py`) instead, then run codegen (see [Code Generation](#code-generation-critical-pattern)).
22+
23+
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`.
24+
25+
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/`.
26+
27+
4. **Always run codegen + format after editing `client.py` or `chat_stream.py`:**
28+
29+
```sh
30+
python scripts/codegen.py --path .
31+
./scripts/format.sh
32+
```
33+
34+
5. **Use project scripts, not raw tool commands.** Run `./scripts/run_tests.sh`, not `pytest` directly. The scripts handle codegen and formatting.
35+
36+
## Architecture
37+
38+
### Package Structure
39+
40+
The SDK is organized into independent sub-packages:
41+
42+
- **`slack_sdk/web/`** — Web API client (sync, async, legacy). Contains auto-generated files (see [Code Generation](#code-generation-critical-pattern))
43+
- **`slack_sdk/webhook/`** — Incoming Webhooks
44+
- **`slack_sdk/socket_mode/`** — Socket Mode with pluggable backends
45+
- **`slack_sdk/oauth/`** — OAuth flows and token storage
46+
- **`slack_sdk/models/`** — Block Kit UI builders
47+
- **`slack_sdk/audit_logs/`**, **`slack_sdk/scim/`** — Enterprise APIs
48+
- **`slack_sdk/signature/`** — Request verification
49+
- **`slack_sdk/http_retry/`** — Retry handlers
50+
- **`slack/`** — Legacy package (maintenance mode, do not modify)
51+
52+
See the repository structure for the complete package layout.
53+
54+
### Code Generation (Critical Pattern)
55+
56+
**NEVER edit these auto-generated files:**
57+
58+
- `slack_sdk/web/async_client.py`
59+
- `slack_sdk/web/legacy_client.py`
60+
- `slack_sdk/web/async_chat_stream.py`
61+
62+
Each contains a header warning:
63+
```text
64+
# DO NOT EDIT THIS FILE
65+
# 1) Modify slack_sdk/web/client.py
66+
# 2) Run `python scripts/codegen.py`
67+
# 3) Run `black slack_sdk/`
68+
```
69+
70+
**How it works:**
71+
72+
1. Edit `slack_sdk/web/client.py` (canonical source for Web API methods)
73+
2. Edit `slack_sdk/web/chat_stream.py` (canonical source for streaming chat)
74+
3. Run `python scripts/codegen.py --path .` to generate async/legacy variants
75+
4. Run `./scripts/format.sh` to format the generated code
76+
77+
The codegen script (`scripts/codegen.py`) automatically transforms sync code into async variants by adding `async def`, `await`, and replacing classes with async equivalents.
78+
79+
### Web API Method Pattern
80+
81+
Every Web API method in `client.py` follows this pattern:
82+
83+
```python
84+
def method_name(
85+
self,
86+
*, # keyword-only arguments
87+
required_param: str,
88+
optional_param: Optional[str] = None,
89+
**kwargs,
90+
) -> SlackResponse:
91+
"""Description of the API method
92+
https://docs.slack.dev/reference/methods/method.name
93+
"""
94+
kwargs.update({"required_param": required_param})
95+
if optional_param is not None:
96+
kwargs.update({"optional_param": optional_param})
97+
return self.api_call("method.name", params=kwargs)
98+
```
99+
100+
Key conventions:
101+
102+
- All parameters are keyword-only (after `*`)
103+
- Required params have no default; optional params default to `None`
104+
- `**kwargs` captures additional/undocumented parameters
105+
- Parameters are collected into `kwargs` dict and passed to `self.api_call()`
106+
- The `api_call` method name uses Slack's dot-notation (e.g., `"chat.postMessage"`)
107+
- Docstrings include a link to the Slack API reference
108+
109+
### Error Types
110+
111+
All SDK exceptions are defined in `slack_sdk/errors/__init__.py` and inherit from `SlackClientError`.
112+
113+
Key exceptions to be aware of:
114+
115+
- **`SlackApiError`** — Raised when the API returns an error response (carries the `response` object)
116+
- **`SlackRequestError`** — Raised when the HTTP request itself fails
117+
- **`BotUserAccessError`** — Raised when using a bot token (`xoxb-*`) for a user-only method
118+
119+
See `slack_sdk/errors/__init__.py` for the complete list of exception types and their usage.
120+
121+
### HTTP Retry Handlers
122+
123+
The `slack_sdk/http_retry/` module provides built-in retry strategies for connection errors, rate limiting (HTTP 429), and server errors (HTTP 500/503).
124+
125+
Key handlers:
126+
127+
- **`ConnectionErrorRetryHandler`** / **`AsyncConnectionErrorRetryHandler`**
128+
- **`RateLimitErrorRetryHandler`** / **`AsyncRateLimitErrorRetryHandler`** (respects `Retry-After` header)
129+
- **`ServerErrorRetryHandler`** / **`AsyncServerErrorRetryHandler`**
130+
131+
Retry intervals can be configured with `BackoffRetryIntervalCalculator` (exponential backoff) or `FixedValueRetryIntervalCalculator`. See `slack_sdk/http_retry/` for implementation details.
132+
133+
### Test Patterns
134+
135+
Tests use `unittest.TestCase` with a mock web API server:
136+
137+
```python
138+
import unittest
139+
from slack_sdk import WebClient
140+
from tests.slack_sdk.web.mock_web_api_handler import MockHandler
141+
from tests.mock_web_api_server import setup_mock_web_api_server, cleanup_mock_web_api_server
142+
143+
class TestFeature(unittest.TestCase):
144+
def setUp(self):
145+
setup_mock_web_api_server(self, MockHandler)
146+
self.client = WebClient(
147+
token="xoxb-api_test",
148+
base_url="http://localhost:8888",
149+
)
150+
151+
def tearDown(self):
152+
cleanup_mock_web_api_server(self)
153+
154+
def test_something(self):
155+
resp = self.client.api_test()
156+
self.assertTrue(resp["ok"])
157+
```
158+
159+
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.
160+
161+
Test directories:
162+
163+
- `tests/` — Unit tests (mirroring `slack_sdk/` structure)
164+
- `tests/slack_sdk/` — Sync tests
165+
- `tests/slack_sdk_async/` — Async variants
166+
- `tests/slack_sdk_fixture/` — Pytest fixtures and test data
167+
- `tests/data/` — JSON fixture files
168+
- `tests/mock_web_api_server/` — Mock Slack API server
169+
- `integration_tests/` — Tests against real Slack APIs (require env tokens)
170+
171+
## Development Commands
172+
173+
### Setup
174+
175+
**Prerequisites:** A Python virtual environment must be activated. See `.github/maintainers_guide.md` for detailed setup instructions using `pyenv` and `venv`.
176+
177+
**Quick check:** Verify venv is active with `echo $VIRTUAL_ENV` (should output a path).
178+
179+
Always use the project scripts instead of calling tools like `pytest` directly.
180+
181+
### Install Dependencies
182+
183+
```sh
184+
./scripts/install.sh
185+
```
186+
187+
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.
188+
189+
### Full Validation
190+
191+
```sh
192+
./scripts/run_validation.sh
193+
```
194+
195+
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`).
196+
197+
### Individual Commands
198+
199+
Available scripts in the `scripts/` directory:
200+
201+
| Task | Command |
202+
| --- | --- |
203+
| Install dependencies | `./scripts/install.sh` |
204+
| Uninstall all packages | `./scripts/uninstall_all.sh` |
205+
| Format code | `./scripts/format.sh` |
206+
| Lint (check formatting) | `./scripts/lint.sh` |
207+
| Run all unit tests | `./scripts/run_tests.sh` |
208+
| Run a specific test | `./scripts/run_tests.sh tests/slack_sdk/web/test_web_client.py` |
209+
| Run type checking | `./scripts/run_mypy.sh` |
210+
| Generate async/legacy code | `python scripts/codegen.py --path .` |
211+
| Build PyPI package | `./scripts/build_pypi_package.sh` |
212+
| Generate API docs | `./scripts/generate_api_docs.sh` |
213+
214+
## Code Style & Tooling
215+
216+
All tooling configuration is defined in the following files:
217+
218+
- **Formatter**: `black` — see `[tool.black]` in `pyproject.toml`
219+
- **Linter**: `flake8` — see `.flake8`
220+
- **Type checker**: `mypy` — see `[tool.mypy]` in `pyproject.toml`
221+
- **Test runner**: `pytest` — see `[tool.pytest.ini_options]` in `pyproject.toml`
222+
- **Coverage**: `pytest-cov` reporting to Codecov
223+
- **Build system**: see `[build-system]` and `[project]` in `pyproject.toml`
224+
225+
**Dependencies:**
226+
227+
- Testing: `requirements/testing.txt`
228+
- Optional runtime: `requirements/optional.txt`
229+
- Dev tools (black, flake8, mypy): `requirements/tools.txt`
230+
231+
## CI Pipeline (GitHub Actions)
232+
233+
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.
234+
235+
## Key Files & Directories
236+
237+
**Source Code:**
238+
239+
- `slack_sdk/` — Main package (active development)
240+
- `slack_sdk/web/client.py`**CANONICAL SOURCE** for all Web API methods
241+
- `slack_sdk/web/chat_stream.py` — Canonical streaming chat client
242+
- `slack_sdk/version.py` — Single source of truth for version
243+
- `slack/` — Legacy package (maintenance mode, DO NOT MODIFY)
244+
245+
**Configuration:**
246+
247+
- `pyproject.toml` — Project metadata, build config, tool settings (black, pytest, mypy)
248+
- `.flake8` — Flake8 linter configuration
249+
- `requirements/*.txt` — Dependency specifications
250+
251+
**Tooling:**
252+
253+
- `scripts/codegen.py` — Generates async/legacy client variants
254+
- `scripts/*.sh` — Development and CI helper scripts
255+
256+
**GitHub & CI/CD:**
257+
258+
- `.github/` — GitHub-specific configuration and documentation
259+
- `.github/workflows/` — Continuous integration pipeline definitions that run on GitHub Actions
260+
- `.github/maintainers_guide.md` — Maintainer workflows and release process
261+
262+
**Documentation:**
263+
264+
- `README.md` — Project overview, installation, and usage examples
265+
- `.github/maintainers_guide.md` — Maintainer workflows and release process
266+
267+
## Common Contribution Workflows
268+
269+
### Adding a New Web API Method
270+
271+
1. Add the method to `slack_sdk/web/client.py` following the existing pattern
272+
2. Run code generation: `python scripts/codegen.py --path .`
273+
3. Run formatter: `./scripts/format.sh`
274+
4. Add tests in `tests/slack_sdk/web/`
275+
5. Validate: `./scripts/run_validation.sh`
276+
277+
### Adding a New Feature to a Non-Web Module
278+
279+
1. Implement the sync version in the appropriate `slack_sdk/` subpackage
280+
2. If the module has async variants, implement those as well (not auto-generated for non-web modules)
281+
3. Add tests mirroring the module structure
282+
4. Validate: `./scripts/run_validation.sh`
283+
284+
### Fixing a Bug
285+
286+
1. Write a test that reproduces the bug
287+
2. Fix the code (if in `client.py`, run codegen afterward)
288+
3. Validate: `./scripts/run_validation.sh`
289+
290+
## Versioning & Releases
291+
292+
- Use the new version mentioned by the maintainer; if they do not provide it, prompt them for it.
293+
- Ensure the new version follows [Semantic Versioning](http://semver.org/) via [PEP 440](https://peps.python.org/pep-0440/)
294+
- Version lives in `slack_sdk/version.py` and is dynamically read by `pyproject.toml`
295+
- Releases are triggered by publishing a GitHub Release, which triggers the PyPI deployment workflow; the maintainer will take care of this
296+
- Commit message format for releases: `chore(release): version X.Y.Z`
297+
298+
## Dependencies
299+
300+
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:
301+
302+
- `aiohttp` — async HTTP client
303+
- `websockets` / `websocket-client` — Socket Mode backends
304+
- `SQLAlchemy` — OAuth token storage
305+
- `boto3` — S3/DynamoDB token storage
306+
- `aiodns` — faster DNS resolution for async
307+
308+
Version constraints for optional and dev dependencies are pinned in the `requirements/` directory.

scripts/install.sh

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
#!/bin/bash
2+
# ./scripts/install.sh
3+
# Installs all project dependencies (testing, optional, and tools)
4+
5+
set -e
6+
7+
script_dir=$(dirname $0)
8+
cd ${script_dir}/..
9+
10+
pip install -U pip
11+
12+
pip install -U -r requirements/testing.txt \
13+
-U -r requirements/optional.txt \
14+
-U -r requirements/tools.txt

scripts/run_integration_tests.sh

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,7 @@ set -e
88
script_dir=`dirname $0`
99
cd ${script_dir}/..
1010

11-
pip install -U pip
12-
pip install -U -r requirements/testing.txt \
13-
-U -r requirements/optional.txt \
14-
-U -r requirements/tools.txt
11+
./scripts/install.sh
1512

1613
echo "Generating code ..." && python scripts/codegen.py --path .
1714
echo "Running black (code formatter) ..." && ./scripts/format.sh --no-install

0 commit comments

Comments
 (0)