Skip to content

Commit 4ff5262

Browse files
Update Desktop API Stainless config and OpenAPI spec
1 parent 6841539 commit 4ff5262

27 files changed

Lines changed: 518 additions & 270 deletions
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
name: CI
2+
on:
3+
pull_request:
4+
branches:
5+
- main
6+
- next
7+
8+
jobs:
9+
detect_breaking_changes:
10+
runs-on: 'ubuntu-latest'
11+
name: detect-breaking-changes
12+
if: github.repository == 'beeper/desktop-api-python'
13+
steps:
14+
- name: Calculate fetch-depth
15+
run: |
16+
echo "FETCH_DEPTH=$(expr ${{ github.event.pull_request.commits }} + 1)" >> $GITHUB_ENV
17+
18+
- uses: actions/checkout@v6
19+
with:
20+
# Ensure we can check out the pull request base in the script below.
21+
fetch-depth: ${{ env.FETCH_DEPTH }}
22+
23+
- name: Install Rye
24+
run: |
25+
curl -sSf https://rye.astral.sh/get | bash
26+
echo "$HOME/.rye/shims" >> $GITHUB_PATH
27+
env:
28+
RYE_VERSION: '0.44.0'
29+
RYE_INSTALL_OPTION: '--yes'
30+
- name: Install dependencies
31+
run: |
32+
rye sync --all-features
33+
- name: Detect removed symbols
34+
run: |
35+
rye run python scripts/detect-breaking-changes.py "${{ github.event.pull_request.base.sha }}"
36+
37+
- name: Detect breaking changes
38+
run: |
39+
# Try to check out previous versions of the breaking change detection script. This ensures that
40+
# we still detect breaking changes when entire files and their tests are removed.
41+
git checkout "${{ github.event.pull_request.base.sha }}" -- ./scripts/detect-breaking-changes 2>/dev/null || true
42+
./scripts/detect-breaking-changes ${{ github.event.pull_request.base.sha }}

.github/workflows/publish-pypi.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,4 +28,4 @@ jobs:
2828
run: |
2929
bash ./bin/publish-pypi
3030
env:
31-
PYPI_TOKEN: ${{ secrets.BEEPER_DESKTOP_PYPI_TOKEN || secrets.PYPI_TOKEN }}
31+
PYPI_TOKEN: ${{ secrets.BEEPER_PYPI_TOKEN || secrets.PYPI_TOKEN }}

.github/workflows/release-doctor.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,4 +18,4 @@ jobs:
1818
run: |
1919
bash ./bin/check-release-environment
2020
env:
21-
PYPI_TOKEN: ${{ secrets.BEEPER_DESKTOP_PYPI_TOKEN || secrets.PYPI_TOKEN }}
21+
PYPI_TOKEN: ${{ secrets.BEEPER_PYPI_TOKEN || secrets.PYPI_TOKEN }}

.stats.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
configured_endpoints: 23
22
openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/beeper%2Fbeeper-desktop-api-611aa7641fbca8cf31d626bf86f9efd3c2b92778e897ebbb25c6ea44185ed1ed.yml
3-
openapi_spec_hash: d6c0a1776048dab04f6c5625c9893c9c
4-
config_hash: 39ed0717b5f415499aaace2468346e1a
3+
openapi_spec_hash: 4840f003552e8b48eb8e689b59a819ef
4+
config_hash: 05ebdec072113f63395372504da98192

README.md

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -218,10 +218,11 @@ from beeper_desktop_api import BeeperDesktop
218218

219219
client = BeeperDesktop()
220220

221-
client.chats.reminders.create(
222-
chat_id="!NCdzlIaMjZUmvmvyHU:beeper.com",
223-
reminder={"remind_at_ms": 0},
221+
chat = client.chats.create(
222+
account_id="accountID",
223+
user={},
224224
)
225+
print(chat.user)
225226
```
226227

227228
## File uploads
@@ -336,10 +337,10 @@ Note that requests that time out are [retried twice by default](#retries).
336337

337338
We use the standard library [`logging`](https://docs.python.org/3/library/logging.html) module.
338339

339-
You can enable logging by setting the environment variable `BEEPER_DESKTOP_LOG` to `info`.
340+
You can enable logging by setting the environment variable `BEEPER_LOG` to `info`.
340341

341342
```shell
342-
$ export BEEPER_DESKTOP_LOG=info
343+
$ export BEEPER_LOG=info
343344
```
344345

345346
Or to `debug` for more verbose logging.
@@ -438,7 +439,7 @@ import httpx
438439
from beeper_desktop_api import BeeperDesktop, DefaultHttpxClient
439440

440441
client = BeeperDesktop(
441-
# Or use the `BEEPER_DESKTOP_BASE_URL` env var
442+
# Or use the `BEEPER_BASE_URL` env var
442443
base_url="http://my.test.server.example.com:8083",
443444
http_client=DefaultHttpxClient(
444445
proxy="http://my.test.proxy.example.com",

api.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ from beeper_desktop_api.types import MessageUpdateResponse, MessageSendResponse
9191
Methods:
9292

9393
- <code title="put /v1/chats/{chatID}/messages/{messageID}">client.messages.<a href="./src/beeper_desktop_api/resources/messages.py">update</a>(message_id, \*, chat_id, \*\*<a href="src/beeper_desktop_api/types/message_update_params.py">params</a>) -> <a href="./src/beeper_desktop_api/types/message_update_response.py">MessageUpdateResponse</a></code>
94-
- <code title="get /v1/chats/{chatID}/messages">client.messages.<a href="./src/beeper_desktop_api/resources/messages.py">list</a>(chat_id, \*\*<a href="src/beeper_desktop_api/types/message_list_params.py">params</a>) -> <a href="./src/beeper_desktop_api/types/shared/message.py">SyncCursorSortKey[Message]</a></code>
94+
- <code title="get /v1/chats/{chatID}/messages">client.messages.<a href="./src/beeper_desktop_api/resources/messages.py">list</a>(chat_id, \*\*<a href="src/beeper_desktop_api/types/message_list_params.py">params</a>) -> <a href="./src/beeper_desktop_api/types/shared/message.py">SyncCursorNoLimit[Message]</a></code>
9595
- <code title="get /v1/messages/search">client.messages.<a href="./src/beeper_desktop_api/resources/messages.py">search</a>(\*\*<a href="src/beeper_desktop_api/types/message_search_params.py">params</a>) -> <a href="./src/beeper_desktop_api/types/shared/message.py">SyncCursorSearch[Message]</a></code>
9696
- <code title="post /v1/chats/{chatID}/messages">client.messages.<a href="./src/beeper_desktop_api/resources/messages.py">send</a>(chat_id, \*\*<a href="src/beeper_desktop_api/types/message_send_params.py">params</a>) -> <a href="./src/beeper_desktop_api/types/message_send_response.py">MessageSendResponse</a></code>
9797

@@ -110,7 +110,7 @@ from beeper_desktop_api.types import (
110110
Methods:
111111

112112
- <code title="post /v1/assets/download">client.assets.<a href="./src/beeper_desktop_api/resources/assets.py">download</a>(\*\*<a href="src/beeper_desktop_api/types/asset_download_params.py">params</a>) -> <a href="./src/beeper_desktop_api/types/asset_download_response.py">AssetDownloadResponse</a></code>
113-
- <code title="get /v1/assets/serve">client.assets.<a href="./src/beeper_desktop_api/resources/assets.py">serve</a>(\*\*<a href="src/beeper_desktop_api/types/asset_serve_params.py">params</a>) -> None</code>
113+
- <code title="get /v1/assets/serve">client.assets.<a href="./src/beeper_desktop_api/resources/assets.py">serve</a>(\*\*<a href="src/beeper_desktop_api/types/asset_serve_params.py">params</a>) -> BinaryAPIResponse</code>
114114
- <code title="post /v1/assets/upload">client.assets.<a href="./src/beeper_desktop_api/resources/assets.py">upload</a>(\*\*<a href="src/beeper_desktop_api/types/asset_upload_params.py">params</a>) -> <a href="./src/beeper_desktop_api/types/asset_upload_response.py">AssetUploadResponse</a></code>
115115
- <code title="post /v1/assets/upload/base64">client.assets.<a href="./src/beeper_desktop_api/resources/assets.py">upload_base64</a>(\*\*<a href="src/beeper_desktop_api/types/asset_upload_base64_params.py">params</a>) -> <a href="./src/beeper_desktop_api/types/asset_upload_base64_response.py">AssetUploadBase64Response</a></code>
116116

pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ dev-dependencies = [
5959
"importlib-metadata>=6.7.0",
6060
"rich>=13.7.1",
6161
"pytest-xdist>=3.6.1",
62+
"griffe>=1",
6263
]
6364

6465
[tool.rye.scripts]

requirements-dev.lock

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ backports-asyncio-runner==1.2.0
3434
certifi==2026.1.4
3535
# via httpcore
3636
# via httpx
37+
colorama==0.4.6
38+
# via griffe
3739
colorlog==6.10.1
3840
# via nox
3941
dependency-groups==1.3.1
@@ -53,6 +55,7 @@ filelock==3.19.1
5355
frozenlist==1.8.0
5456
# via aiohttp
5557
# via aiosignal
58+
griffe==1.14.0
5659
h11==0.16.0
5760
# via httpcore
5861
httpcore==1.0.9

scripts/detect-breaking-changes

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
#!/usr/bin/env bash
2+
3+
set -e
4+
5+
cd "$(dirname "$0")/.."
6+
7+
echo "==> Detecting breaking changes"
8+
9+
TEST_PATHS=( tests/api_resources tests/test_client.py tests/test_response.py )
10+
11+
for PATHSPEC in "${TEST_PATHS[@]}"; do
12+
# Try to check out previous versions of the test files
13+
# with the current SDK.
14+
git checkout "$1" -- "${PATHSPEC}" 2>/dev/null || true
15+
done
16+
17+
# Instead of running the tests, use the linter to check if an
18+
# older test is no longer compatible with the latest SDK.
19+
./scripts/lint

scripts/detect-breaking-changes.py

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
from __future__ import annotations
2+
3+
import sys
4+
from typing import Iterator
5+
from pathlib import Path
6+
7+
import rich
8+
import griffe
9+
from rich.text import Text
10+
from rich.style import Style
11+
12+
13+
def public_members(obj: griffe.Object | griffe.Alias) -> dict[str, griffe.Object | griffe.Alias]:
14+
if isinstance(obj, griffe.Alias):
15+
# ignore imports for now, they're technically part of the public API
16+
# but we don't have good preventative measures in place to prevent
17+
# changing them
18+
return {}
19+
20+
return {name: value for name, value in obj.all_members.items() if not name.startswith("_")}
21+
22+
23+
def find_breaking_changes(
24+
new_obj: griffe.Object | griffe.Alias,
25+
old_obj: griffe.Object | griffe.Alias,
26+
*,
27+
path: list[str],
28+
) -> Iterator[Text | str]:
29+
new_members = public_members(new_obj)
30+
old_members = public_members(old_obj)
31+
32+
for name, old_member in old_members.items():
33+
if isinstance(old_member, griffe.Alias) and len(path) > 2:
34+
# ignore imports in `/types/` for now, they're technically part of the public API
35+
# but we don't have good preventative measures in place to prevent changing them
36+
continue
37+
38+
new_member = new_members.get(name)
39+
if new_member is None:
40+
cls_name = old_member.__class__.__name__
41+
yield Text(f"({cls_name})", style=Style(color="rgb(119, 119, 119)"))
42+
yield from [" " for _ in range(10 - len(cls_name))]
43+
yield f" {'.'.join(path)}.{name}"
44+
yield "\n"
45+
continue
46+
47+
yield from find_breaking_changes(new_member, old_member, path=[*path, name])
48+
49+
50+
def main() -> None:
51+
try:
52+
against_ref = sys.argv[1]
53+
except IndexError as err:
54+
raise RuntimeError("You must specify a base ref to run breaking change detection against") from err
55+
56+
package = griffe.load(
57+
"beeper_desktop_api",
58+
search_paths=[Path(__file__).parent.parent.joinpath("src")],
59+
)
60+
old_package = griffe.load_git(
61+
"beeper_desktop_api",
62+
ref=against_ref,
63+
search_paths=["src"],
64+
)
65+
assert isinstance(package, griffe.Module)
66+
assert isinstance(old_package, griffe.Module)
67+
68+
output = list(find_breaking_changes(package, old_package, path=["beeper_desktop_api"]))
69+
if output:
70+
rich.print(Text("Breaking changes detected!", style=Style(color="rgb(165, 79, 87)")))
71+
rich.print()
72+
73+
for text in output:
74+
rich.print(text, end="")
75+
76+
sys.exit(1)
77+
78+
79+
main()

0 commit comments

Comments
 (0)